# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from datetime import timedelta
import pytz,time, logging
import openerp.addons.decimal_precision as dp
import pytz, json
from openerp.tools import float_round
from openerp import models, SUPERUSER_ID
from openerp.http import request
from openerp.osv import fields
from openerp.osv import osv
from openerp.tools.translate import _
from openerp.tools import float_compare
from openerp.addons.web.http import request
ISODATEFORMAT = '%Y-%m-%d'
ISODATETIMEFORMAT = "%Y-%m-%d %H:%M:%S"
# <field name = "move_lines" context = "{'address_in_id': partner_id, 'form_view_ref': 'stock.view_move_picking_form',
#                                        'tree_view_ref': 'stock.view_move_picking_tree', 'default_picking_type_id': picking_type_id, 'default_picking_id': active_id}" / >
_logger = logging.getLogger(__name__)

class sale_order_line_change(osv.osv):
    _name = 'sale.order.line.change'
    _decription = 'record sale_order_linde change'

    # def write(self, cr, uid, ids, vals, context=None):
    #     # 当变化表写入了PO_id的时候说明这条记录已经被处理过了。
    #     super(sale_order_line_change, self).write(
    #         cr, uid, ids, vals, context=context)
    #     for order_line_obj in self.browse(cr, uid, ids, context=context):
    #         order_line_change_objs = self.search(
    #             cr, uid, [('po_id', '=', False), ('so_id', '=', order_line_obj.so_id.id)], context=context)
    #         if not order_line_change_objs:
    #             self.pool.get("sale.order").write(
    #                 cr, uid, order_line_obj.so_id.id, {'purchase_managed': True}, context=context)
    #         else:
    #             self.pool.get("sale.order").write(
    #                 cr, uid, order_line_obj.so_id.id, {'purchase_managed': False}, context=context)
    #     return ids
    _columns = {
        'so_id': fields.many2one('sale.order', u'订单ID', required='True', select=True),
        'product_id': fields.many2one('product.product', u'产品ID', select=True),
        'change_quantity': fields.integer(u'变化数量', required='True'),
        'po_id': fields.many2one('purchase.order', u'生成的采购订单的ID', select=True),
        'so_line_id': fields.many2one('sale.order.line', u'对应的销售订单行id', select=True),
        'prod_spec': fields.char(u'型号备注'),
        # 'expected_delivery_time': fields.related('so_id', 'expected_delivery_time', type='datetime', string='期望发货日期', store=True, readonly="1"),
        'expected_delivery_datetime': fields.datetime(u'期望发货日期'),
        'po_line_id': fields.many2one('purchase.order.line', u'对应的采购订单行id', select=True),
        'stock_location_id': fields.many2one('stock.location', u'库位'),
        'stock_picking_type_id': fields.many2one('stock.picking.type', string=u'入库操作类型'),
        'bom_id': fields.many2one('mrp.bom', string='产品bom'),
        'purchase_user_id': fields.related('po_id', 'create_uid', relation='res.users', type='many2one', string='采购员')
    }


class profit_deduct_type(osv.osv):
    _name = 'profit.deduct.type'
    _rec_name = 'name'
    # 銷售訂單需要的一個字段，可能不停的變動
    _columns = {
        'name': fields.selection(
            [(u'主推', u'主推'), (u'常规', u'常规'),
             (u'特殊', u'特殊'), (u'无提成', u'无提成'), (u'特殊1', u'特殊1'), (u'特殊2', u'特殊2'), (u'常规1', u'常规1'),
             (u'常规2', u'常规2'), (u'主推1', u'主推1'), (u'主推2', u'主推2'), (u'主推3', u'主推3'), (u'主推4', u'主推4'),
             (u'无提成61', u'无提成61'), ], u'提成类型', required=True),
        # 主推：0.5～, 常规0.2~ <0.5，特殊0.05～ <0.2
        'gross_profit_margin': fields.float(u'最高毛利率(顺加毛利:成本＊毛利率)'),
        'gross_profit_margin_min': fields.float(u'起始毛利率(顺加毛利:成本＊毛利率)'),
        'gross_profit_margin_top': fields.float(u'最高毛利率(顺加毛利:成本＊毛利率)'),
        # 销售：主推 2%  常规1.5%  特殊 0.5%
        'sales_staff_commission': fields.float(u'销售员提成比例', digits_compute=dp.get_precision('Product Price')),
        # 助理  主推 1.1%，常规 0.8% 特殊 0.3%
        'sales_assistant_commission': fields.float(u'销售助理提成比例', digits_compute=dp.get_precision('Product Price')),
        'end_date': fields.date(u'到期日'),
    }


class sale_order(osv.osv):
    _name = 'sale.order'
    _inherit = ['sale.order', 'china.city.county']

    def copy(self, cr, uid, ids, default=None, context=None):
        for order in self.browse(cr, uid, ids, context=context):
            if order.is_card:
                raise osv.except_osv(u'错误', u'卡券订单暂时不支持复制，请重新下单！')
        return super(sale_order, self).copy(cr, uid, ids, default=default, context=context)

    def unlock_inventory(self, cr, uid, ids, context=None):
        #接触对销售订单产品的锁定,只包含 采购入库时自动入库的部分!
        quant_obj = self.pool.get('stock.quant')
        for sale_rows in self.browse(cr, uid, ids, context=context):
            quant_ids = quant_obj.search(cr, SUPERUSER_ID, [('reservation_id', '!=', False),
                                                   ('so_id', '=', sale_rows.id)], context=context)
            quant_obj.write(cr, SUPERUSER_ID, quant_ids, {'reservation_id': False, 'so_id': False, 'so_line_id': False}, context=context)
        return True

    def onchange_shipping_type_sale(self, cr, uid, ids, shipping_type_sale, context=None):
        if shipping_type_sale == 'direct_delivery':
            return {'warning': {'title': u'警告', 'message': u'你没有权限选择这种送货方式！'},
                    'value': {'shipping_type_sale': 'sale_delivery'}}

    def get_sale_order_location_id(self, cr, uid, order_id, context=None):
        # 根据销售订单的 order_type 得到本销售订单的库位的设置.
        order_objs = self.browse(cr, uid, order_id, context=context)
        location_id = False
        if order_objs.order_type_id:
            location_id = order_objs.order_type_id.stock_location_id.id
        else:
            model_data_obj = self.pool.get('ir.model.data')
            location_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'stock.stock_location_stock')
        if not location_id:
            raise osv.except_osv(
                u'错误', u'%s的订单类型不存在对应的库位,请重新设置后再进行操作！' % order_objs.name)

        location_ids=self.pool.get('stock.location').get_child_location_ids(cr,uid,location_id,context=context)
        return (location_id,location_ids)

    def get_sale_order_in_picking_type_id(self, cr, uid, order_id, context=None):
        # 根据销售订单的 order_type 得到本销售订单的库位的设置.
        if isinstance(order_id, list):
            order_id = order_id and order_id[0]
        order_objs = self.browse(cr, uid, order_id, context=context)
        if order_objs.order_type_id and order_objs.order_type_id.stock_picking_type_id:
            picking_type_id = order_objs.order_type_id.stock_picking_type_id.id
        else:
            model_data_obj = self.pool.get('ir.model.data')
            picking_type_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'stock.picking_type_in')
        return picking_type_id

    def resetting_refresh_available_qty(self, cr, uid, ids, context=None):
        """ 销售查看仓库可用库存  """
        # stock_quant = self.pool.get("stock.quant")
        sale_order_obj = self.browse(cr, uid, ids, context=context)
        product_id_order_dcit = {}
        for order_line in sale_order_obj.order_line:
            if order_line.product_id.type=='service':
                continue
            product_id_order_dcit.update({order_line.product_id.id: order_line})
        if product_id_order_dcit:
            location_ids = self.get_sale_order_location_id(cr, uid, sale_order_obj.id, context=context)[1]
            result_dict = self.pool.get(
                'product.product').get_product_location_available_qty(cr, uid, product_id_order_dcit.keys(),
                                                                      so_id=sale_order_obj.id, context=context)
            for product_id, order_line in product_id_order_dcit.items():
                available_without_reservation = sum([result_dict.get((product_id, order_line.prod_spec or None, location_id),0)
                                                     for location_id in location_ids])
                # 1.订单行数量大于零 则进行 产品库存检查
                # 2. 订单行数量小于零 则 不进行库存检查,直接产品数量 减去 已建发货单数量
                order_line.write({'refresh_available_qty': available_without_reservation})
        return True

    def resetting_delivery_quantity_qty(self, cr, uid, ids, context=None):
        """ 重置 本次发货数量  并显示可用库存(用以核对) """
        # stock_quant = self.pool.get("stock.quant")
        sale_order_obj = self.browse(cr, uid, ids, context=context)
        product_id_order_dcit = {}
        for order_line in sale_order_obj.order_line:
            if order_line.product_id.type == 'service':
                continue
            # 计算被保留的产品的数量
            # stock_quant_ids = stock_quant.search(cr, uid, [(
            #     "product_id", "=", order_line.product_id.id), ("reservation_id", "!=", False), ('location_id.usage', '=', 'internal')], context=context)
            # for stock_quant_obj in stock_quant.browse(cr, uid, stock_quant_ids, context=context):
            #     reservation_qty += stock_quant_obj.qty
            product_id_order_dcit.update({order_line.product_id.id: order_line})
        if product_id_order_dcit:
            result_dict = self.pool.get(
                'product.product').get_product_location_available_qty(cr, uid, product_id_order_dcit.keys(),
                                                                      so_id=sale_order_obj.id, context=context)
            sale_order_enough_qty = True
            location_ids = self.get_sale_order_location_id(cr, uid, sale_order_obj.id, context=context)[1]
            for product_id, order_line in product_id_order_dcit.items():
                available_without_reservation = sum([result_dict.get((product_id, order_line.prod_spec or None, location_id),0)
                                                     for location_id in location_ids])
                # 1.订单行数量大于零 则进行 产品库存检查
                # 2. 订单行数量小于零 则 不进行库存检查,直接产品数量 减去 已建发货单数量
                if not order_line.purchase_processed:
                    order_line.write({'delivery_quantity_qty': 0})
                elif order_line.product_uom_qty > 0:
                    # 1.订单行的产品数量必须 大于零 才会进行计算
                    # 2. 的可用数量大于未建出库单 的数量
                    order_line.write({'delivery_quantity_qty': order_line.product_uom_qty - order_line.delivered_qty})
                else:
                    order_line.write({'delivery_quantity_qty': abs(order_line.product_uom_qty) - order_line.delivered_qty,
                                      })
                order_line.write({'refresh_available_qty': available_without_reservation})
            if sale_order_enough_qty:
                sale_order_obj.write({'last_check_stock_date': datetime.now().strftime(ISODATETIMEFORMAT)})
            else:
                sale_order_obj.write({'last_check_stock_date': False})
        return True

    def name_search(self, cr, uid, name, args=None, operator='ilike', context=None, limit=100):
        # 當搜索销售订单的时候进行 一系列操作 ，返回的符合条件的结果
        args = args or []
        if context.get('menu_judge_view_id') == 'dftg_action_orders':
            # menu_judge_view_id == dftg_action_orders 是在假单页面一个特别的context 所以用他进行判断
            result_list = []
            rusult_list_id = self.search(cr, uid, [('name', 'ilike', name)], context=context, limit=limit)
            # 如果是真单 则返回真单的 [(id,name)] 如果是假单则返回 这个 假单对应的真单
            if rusult_list_id:
                result_list = []
                for sale_order_obj in self.browse(cr, uid, rusult_list_id, context=context):
                    fake_order_id = self.search(cr, uid, [('real_so_id', '=', sale_order_obj.id)], context=context)
                    if not fake_order_id:
                        result_list.append((sale_order_obj.real_so_id.id, sale_order_obj.real_so_id.name))
                    else:
                        result_list.append((sale_order_obj.id, sale_order_obj.name))
            return result_list
        else:
            return super(sale_order, self).name_search(cr, uid, name, args,
                                                       operator=operator, context=context, limit=limit)

    def view_official_invoice_rel(self, cr, uid, ids, context=None):
        """ 删除销售订单的时候必须先要删除 纸质发票申请 这个方法就是为了方便的删除纸质发票申请"""
        sale_order_objs = self.browse(cr, uid, ids, context=context)
        # 返回到 新生成的假单的界面
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')
        result = mod_obj.get_object_reference(
            cr, uid, 'dftg_ext', 'sale_order_invoice_action')
        id = result and result[1] or False
        result = act_obj.read(cr, uid, [id], context=context)[0]
        res = mod_obj.get_object_reference(
            cr, uid, 'dftg_ext', 'official_invoice_form_sale')
        result['views'] = [(res and res[1] or False, 'form')]
        result['res_id'] = sale_order_objs.sale_order_invoice.id or False
        return result
      
    def unlink(self, cr, uid, ids, context=None):
        # 删除销售订单的时候要进行 判断 如果有纸质发票就 不让删除。
        for sale_order_row in self.browse(cr, uid, ids, context=context):
            if not sale_order_row.real_so_id:
                if sale_order_row.sale_order_invoice:
                    raise osv.except_osv(
                        u'错误', u'%s存在相关的纸质发票的申请，请删除相关的纸质发票的记录再进行操作！' % sale_order_row.name)
            picking_ids = self.pool['stock.picking'].search(cr, uid, [('so_id', '=', sale_order_row.id),
                                                                      ('state', '!=', 'cancel')])
            if picking_ids:
                raise osv.except_osv(u'错误', u"请先联系仓库取消出库单，之后才能删除订单")
        self.unlock_inventory(cr, uid, ids, context=context)
        return super(sale_order, self).unlink(cr, uid, ids, context=context)

    def action_cancel(self, cr, uid, ids, context=None):
        # 取消报价单时 这个报价单也就失效了， 要进行相应的单据的回写，
        # 1.变更表数据的回写
        # 2.订单有未取消的库存单据请取消
        # 3.订单有未取消的发票请取消
        order_line_obj = self.pool.get('sale.order.line')
        order_line__change_obj = self.pool.get("sale.order.line.change")
        picking_obj = self.pool.get("stock.picking")
        return_record_obj = self.pool.get('card.return.record')
        for sale_order_row in self.browse(cr, uid, ids, context=context):
            if sale_order_row.is_locked_quant:
                self.unlock_inventory(cr, uid, sale_order_row.id, context=context)
            if sale_order_row.state == 'cancel':
                raise osv.except_osv(u'错误', u"销售订单已经取消，请刷新后在进行操作！")
            return_record_id = return_record_obj.search(cr, uid, [('new_order_id', '=', sale_order_row.id)], context=context)
            if return_record_id:
                return_record_obj.back_return_card_order(cr, uid, return_record_id, context=context)
            picking_ids = picking_obj.search(cr, uid, [('so_id', '=', sale_order_row.id),
                                                       ('state', 'not in', ('cancel', 'done'))])
            if picking_ids and not sale_order_row.is_return and not context.get('can_cancel', False):
                raise osv.except_osv(u'错误', u"请先联系仓库取消出库单，之后才能取消订单")
            if sale_order_row.is_return:
                picking_obj.action_cancel(cr, uid, picking_ids, context=context)
            sale_order_row.write({'sale_order_invoice': False})
            for invoice_id_obj in self.pool.get("account.invoice").browse(cr, uid,
                                            [invoice.id for invoice in sale_order_row.invoice_ids], context=context):
                if invoice_id_obj.state != 'cancel':
                    raise osv.except_osv(
                        u'错误', u"订单有未取消的发票请取消%s发票后再进行操作!" % invoice_id_obj.name)

            for stock_obj in picking_obj.browse(cr, uid, picking_obj.search(cr, uid, [('so_id', '=', sale_order_row.id)],
                                                                           context=context), context=context):
                if stock_obj.state not in ('cancel', 'done'):
                    raise osv.except_osv(
                        u'错误', u"订单有未取消的库存单据请取消%s单据后再进行操作!" % stock_obj.name)
            for line in sale_order_row.order_line:
                if line.shipped_qty != 0:
                    raise osv.except_osv(
                        u'错误', u"%s产品已出／入库%s个不能取消!（请先处理以出／入库的产品）"%
                               (line.name, line.shipped_qty, line.delivered_qty))
            for order_id in ids:
                temporary_id = self.pool.get("sale.order.temporary").search(cr, uid, [('sale_id',
                                                                                '=', order_id)], context=context)
                temporary_row = self.pool.get("sale.order.temporary").browse(cr, uid, temporary_id, context=context)
                temporary_table_data = eval(temporary_row.sale_order_dict or '{}')
                location_id = self.get_sale_order_location_id(cr, uid, sale_order_row.id, context=context)[0]
                # 处理被采购处理过被删除的产品 新建 订单变化表
                temporary_table_lists = temporary_table_data.keys()
                temporary_table_list_prod = [
                    (table_list, temporary_table_data.get(table_list)[1]) for table_list in temporary_table_lists]
                product_ids = {}
                prodcut_list = [(key, product_ids.get(key)) for key in product_ids.keys()]
                for new_change_product, prod_spec in (set(temporary_table_list_prod) - set(prodcut_list)):
                    if temporary_table_data.get(new_change_product)[0] != 0:
                        sale_order_line = order_line_obj.search(cr, uid, [('order_id', '=', ids[0]),
                                                                          ('product_id', '=', new_change_product),
                                                                          ('prod_spec', '=', prod_spec)], context=context)
                        order_line__change_obj.create(cr, SUPERUSER_ID, {
                            'so_id': ids[0],
                            "product_id": new_change_product,
                            "change_quantity": - temporary_table_data.get(new_change_product)[0],
                            'prod_spec': prod_spec,
                            'stock_location_id': location_id,
                            'so_line_id': sale_order_line and sale_order_line[0],
                            'expected_delivery_datetime': sale_order_row.expected_delivery_time},
                                                      context=context)
        return super(sale_order, self).action_cancel(cr, uid, ids, context=context)

        # 对于没有发货但是已经 处理变成记录的 后续操作的修正
        # for order_line in sale_order_objs.order_line:
        #     line_change_ids = self.pool.get('sale.order.line.change').search(
        #         cr, uid, [('so_id', '=', sale_order_objs.id), ('so_line_id', '=', order_line.id)], context=context)
        #     left_num = 0
        #     for line_change_objs in self.pool.get('sale.order.line.change').browse(cr,
        #  uid, line_change_ids, context=context):
        #         left_num += line_change_objs.change_quantity
        #     if left_num != 0:
        #         self.pool.get('sale.order.line.change').create(
        #             cr, uid, {'so_id': sale_order_objs.id, 'so_line_id': order_line.id,
        # 'product_id': order_line.product_id.id, 'change_quantity': -left_num, 'prod_spec': order_line.prod_spec,
        #
        #  'stock_location_id':
        # self.get_sale_order_location_id(cr, uid, sale_order_objs.id, context=context)[0]}, context=context)

    def check_expected_time_confirmed(self, cr, uid, order_row, context=None):
        if order_row.is_need_purchase_audit:
            self.raise_error('采购未确认发货日期,不能建立出库单发货！')
        return True

    # 锁定库存操作的，的第二步，直接创建采购单
    # 并且直接　检查可用　
    # 销售订单变更记录　的采购单　po_id 填充为　-1　自动生成的一条　记录用来记录　已锁的变更记录
    def create_out_going_picking(self, cr, uid, order_id, assgin=False, context=None):
        res = {}
        stock_picking_type_obj = self.pool.get("stock.picking.type")
        stock_location_obj = self.pool.get("stock.location")
        stock_picking_obj = self.pool.get("stock.picking")
        sale_order_line_obj = self.pool.get("sale.order.line")
        stock_move_obj = self.pool.get("stock.move")
        picking_type_out_id = stock_picking_type_obj.search(
            cr, uid, [('code', '=', 'outgoing')], context=context)
        picking_type_out_row = stock_picking_type_obj.browse(
            cr, uid, picking_type_out_id, context=context)[0]  # warehouse_id
        sale_order_row = self.pool.get("sale.order").browse(
            cr, uid, order_id, context=context)
        res.update({"so_id": sale_order_row.id, "partner_id": sale_order_row.partner_id.id,
                    "origin": sale_order_row.name, "picking_type_id": picking_type_out_id[0],
                    "move_type": "one",
                    })
        # 查找是否有假单
        fake_so_ids = self.pool.get("sale.order").search(cr, uid, [('real_so_id', '=', order_id)], context=context)
        move_line_in_ids = []
        move_line_out_ids = []
        #取得客户库位
        stock_location_ids = stock_location_obj.search(cr, uid, [('usage', '=', 'customer')], context=context)
        if stock_location_ids:
            stock_customer_location_id = stock_location_ids[0]
        else:
            stock_customer_location_id = False

        res.update({"shipping_type_sale": sale_order_row.shipping_type_sale})
        res.update({'name': self.pool.get('ir.sequence').get_id(
            cr, uid, picking_type_out_row.sequence_id.id, 'id', context=context)})
        # 建立空的picking单
        picking_out_id = stock_picking_obj.create(cr, uid, res, context=context)
        picking_in_id = None #入库单默认没有

        #如果是退货销售订单或者换货销售订单,则建立入库picking
        if sale_order_row.is_exchange or sale_order_row.is_return:
            picking_type_in_id = stock_picking_type_obj.search(
                cr, uid, [('code', '=', 'incoming')], context=context)
            picking_type_in_obj = stock_picking_type_obj.browse(
                cr, uid, picking_type_in_id, context=context)[0]  # warehouse_id
            # 退货单或者换货单所需建立的收货单
            res.update({"picking_type_id": picking_type_in_id[0], 'name': self.pool.get(
                'ir.sequence').get_id(cr, uid, picking_type_in_obj.sequence_id.id, 'id', context=context)})
            # 建立空的picking入库单
            picking_in_id = stock_picking_obj.create(cr, uid, res, context=context)

        for order_line in sale_order_row.order_line:
            if order_line.product_id.type == 'service':
                continue
            move_line_out_data = {}
            move_line_in_data = {}
            sale_order_line_obj.write(cr, uid, order_line.id, {"delivered_qty": 0}, context=context)
            #建立出库单move记录
            if order_line.product_uom_qty > 0:

                so_cl_ids = self.pool.get('sale.order.line.change').search(cr, uid,
                                                                           [('so_line_id', '=', order_line.id)],
                                                                           context=context)
                move_line_out_data.update(
                    {"picking_type_id": picking_type_out_id[0], "product_uom_qty": order_line.product_uom_qty,
                     "product_id": order_line.product_id.id, "name": order_line.name,
                     "location_dest_id": picking_type_out_row.default_location_dest_id.id,
                     "location_id": sale_order_row.order_type_id and sale_order_row.order_type_id.stock_location_id.id or picking_type_out_row.default_location_src_id.id,
                     # so_change_id 主要針對卡券提货记录（逻辑上还是有缺陷不能适应大部分订单， move 上无法记录多个change_ids 和                 #
                     # 2. change_id 表上的order_line 字段可能确失导致不对， 已处理的销售订单行删除，并添加一个相同数量产品的行，# ）
                     "picking_id": picking_out_id, 'so_change_id': so_cl_ids and so_cl_ids[0], 'so_line_id':order_line.id,
                     "product_uom": order_line.product_id.uom_id.id, 'prod_spec': order_line.prod_spec})
                sale_order_line_obj.write(cr, uid, order_line.id,
                                          {"delivered_qty": order_line.delivered_qty + order_line.product_uom_qty},
                                          context=context)
                move_line_out_ids.append(stock_move_obj.create(
                    cr, uid, move_line_out_data, context=context))
            #建立入库单move记录
            elif order_line.product_uom_qty < 0:
                card_number = None
                if sale_order_row.apply_delivery_id:
                    card_number = u'卡券' + sale_order_row.apply_delivery_id.copy_code
                sale_order_code = sale_order_row.name
                move_line_in_data.update({"picking_type_id": picking_type_in_id[0],
                                          "product_uom_qty": abs(order_line.product_uom_qty),  # default_location_src_id
                                          "product_id": order_line.product_id.id,
                                          "name": order_line.name,
                                          "origin": card_number or sale_order_code,
                                          "location_dest_id": sale_order_row.order_type_id and sale_order_row.order_type_id.stock_location_id.id or picking_type_in_obj.default_location_dest_id.id,
                                          "location_id": stock_customer_location_id or picking_type_in_obj.default_location_src_id.id,
                                          "picking_id": picking_in_id,
                                          "product_uom": order_line.product_id.uom_id.id,
                                          })
                sale_order_line_obj.write(cr, uid, order_line.id, {"delivered_qty": order_line.delivered_qty + abs(order_line.product_uom_qty),
                                                                   "delivery_quantity_qty": 0}, context=context)
                move_line_in_ids.append(stock_move_obj.create(cr, uid, move_line_in_data, context=context))

        picking_list = [picking_out_id]
        if picking_in_id:
            picking_list.append(picking_in_id)

        # picking单标记为可用(确认)
        stock_picking_obj.action_confirm(cr, uid, picking_list, context=context)
        # picking单检查可用
        if assgin:
            stock_picking_obj.action_assign(cr, uid, picking_list, context)
        # 取得所有picking单
        picking_rows = stock_picking_obj.browse(cr, uid, picking_list, context=context)
        # 如果picking单都已经检查可用通过
        if all(picking_state.state == 'assigned' for picking_state in picking_rows):
            #如果不存在假单
            if not fake_so_ids:
                for change_id_row in sale_order_row.order_line_change_id:
                    if not change_id_row.po_id.id:
                        change_id_row.write({'po_id': -1})
            return True
        else:
            if assgin:
                [stock_picking_obj.do_unreserve(cr, uid, picking_list, context=context)
                    for picking in picking_rows if picking.state != 'assigned']
            # if not fake_so_ids:
            #     raise osv.except_osv(u'错误', u'库存锁定失败')
            return False
        return False

    def card_locked_inventory_operation(self, cr, uid, ids, context=None):
        return self.create_out_going_picking(cr, uid, ids, assgin=True, context=context)

    
    #　库存锁定功能　的前期的判断逻辑　库存是否充足　
    def locked_inventory_operation(self, cr, uid, ids, context=None):
        order = self.browse(cr, 1, ids, context=context)
        product_qty_dict = {}
        location_ids = self.get_sale_order_location_id(cr, uid, order.id, context=context)[1]
        [product_qty_dict.update({(line.product_id.id, line.prod_spec): line.product_uom_qty + product_qty_dict.get(
        (line.product_id.id, line.prod_spec), 0)}) for line in order.order_line if line.product_id.type != 'service']
        if product_qty_dict:
            result_dict = self.pool.get(
                'product.product').get_product_location_available_qty(cr, uid, [product_id_tuple[0]
                                                                    for product_id_tuple in product_qty_dict.keys()],
                                                                      so_id=order.id, context=context)
            quant_qty_is_enough = True
            for prodcut_spec, product_qty in product_qty_dict.items():
                quant_qty = sum(
                    [result_dict.get((prodcut_spec[0], prodcut_spec[1] or None, location_id), 0) for location_id in
                     location_ids])
                if quant_qty < product_qty:
                    quant_qty_is_enough = False
                    break
            if quant_qty_is_enough:
                try:
                    return self.create_out_going_picking(cr, uid, order.id, assgin=True, context=context)
                except Exception as e:
                    _logger.error('%r generated an exception' % (e))
                    return False
            # else:  # bom 产品 有库存转换之后再进行锁定库存。： 功能取消
            #     try:
            #         cr.execute("""
            #         SELECT * FROM mrp_bom mp WHERE mp.product_tmpl_id in
            #         (SELECT pp.product_tmpl_id FROM product_product as pp WHERE pp.id in %s)
            #         """ % str([product_id_tuple[0] for product_id_tuple in
            #                    product_qty_dict]).replace('[', '(').replace(']', ')'))
            #         fetchall_dict = cr.fetchall()
            #         if fetchall_dict and fetchall_dict[0]:
            #             reuturn_val = self.create_out_going_picking(cr, uid, order.id, context=context)
            #             if reuturn_val:
            #                 return True
            #     except Exception as e:
            #         cr.rollback()
            #         if e and e[1] == '出库数量超过仓库中可用库存！':
            #             return False
            #         else:
            #             raise osv.except_osv(*e)
            return False

    def get_order_line_product_bom_message(self ,cr,uid,bom_id,qty,prod_spec,context=None):
        product_dict_message = {}
        for bom in self.pool.get('mrp.bom').browse(cr, uid ,bom_id,context=context):
            for product_bom_line in bom.bom_line_ids:
                product_dict_message.update({product_bom_line.product_id.id:[1.0*qty*product_bom_line.product_qty*bom.product_qty,
                                                                             prod_spec]})
        return product_dict_message

    def _get_temporary_table_data(self, cr, uid, order_id, context=None):
        temporary_obj = self.pool.get("sale.order.temporary")
        temporary_id = temporary_obj.search(cr, uid, [('sale_id', '=', order_id)], context=context)
        temporary_row = temporary_obj.browse(cr, uid, temporary_id, context=context)
        return temporary_row, temporary_row and eval(temporary_row.sale_order_dict) or {}

    def judge_order_line_detail(self, cr, uid, ids, location_id, sale_order_row, temporary_table_data, context=None):
        product_ids_spec_map = {}
        change_message = {}
        service_order, is_financial_confirm, have_positive_number, have_negative_number = True, False, False, False
        for sales_order_line in sale_order_row.order_line:
            product_uom_qty = sales_order_line.product_uom_qty
            if sales_order_line.product_id.is_financial_confirm:
                is_financial_confirm = True
            if product_uom_qty > 0:
                have_positive_number = True
            elif product_uom_qty < 0:
                have_negative_number = True
            # 判断是否为纯服务订单
            if sales_order_line.product_id.type != 'service':
                service_order = False
            if product_uom_qty >= 0 and (
                            sales_order_line.product_id.type != 'service' or sales_order_line.product_id.purchase_ok):
                # 只有订单行数量大于零才进行操作
                bom_id = self.pool.get('mrp.bom').search(cr, uid,  [('product_tmpl_id', '=',
                                                           sales_order_line.product_id.product_tmpl_id.id)],
                                                         context=context)
                if bom_id:
                    bom_dict_product_qty = \
                        self.get_order_line_product_bom_message(cr, uid, bom_id, product_uom_qty,
                                                                sales_order_line.prod_spec, context=context)
                else:
                    bom_dict_product_qty = {
                        sales_order_line.product_id.id: [product_uom_qty, sales_order_line.prod_spec]}

                for product_id, qty_prod_spec in bom_dict_product_qty.items():
                    # 暂存 产品的 数量型号等信息 在另外一个方法里面进行处理
                    product_ids_spec_map.update({product_id: [product_ids_spec_map.get(product_id, [0, False])[0] + \
                                                              qty_prod_spec[0], qty_prod_spec[1]]})
                    change_message.update(self.handle_change_line(cr, uid, product_id, qty_prod_spec,
                                                                  temporary_table_data, sales_order_line,
                                                                  bom_id, location_id, context=context))
        return {
            'change_message': change_message,
            'product_ids_spec_map': product_ids_spec_map,
            'service_order': service_order,
            'is_financial_confirm': is_financial_confirm,
            'have_positive_number': have_positive_number,
            'have_negative_number': have_negative_number
        }

    def handle_change_line(self, cr, uid, product_id, qty_prod_spec, temporary_table_data, sales_order_line,
                           bom_id, location_id, context=None):
        # 处理一般的销售v订单行变更
        change_message = {}
        order_line_cg_obj = self.pool.get("sale.order.line.change")
        change_qty = qty_prod_spec[0] - temporary_table_data.get(product_id, [0, False])[0]
        solc_ids_no_treat = order_line_cg_obj.search(cr, uid, [('so_id', '=', sales_order_line.order_id.id),
                                                                ('bom_id', '=', bom_id or False),
                                                                ('product_id', '=', product_id),
                                                                ('prod_spec', '=', qty_prod_spec[1]),
                                                                ('po_id', '=', False)],
                                                          context=context)
        # 如果存在未处理的 就在未处理的行上进行更新 有多行的话就只对其中一行进行重写
        if solc_ids_no_treat:
            change_row = order_line_cg_obj.browse(cr, uid, solc_ids_no_treat[0], context=context)
            if change_row.change_quantity + change_qty == 0:
                change_row.unlink()
            else:
                change_row.write({'change_quantity': change_row.change_quantity + change_qty})
        else:
            change_message.update({product_id: [qty_prod_spec[1], change_qty]})
            self.create_positive_change_line(cr, uid, [sales_order_line.order_id.id], product_id, location_id,
                                             qty_prod_spec, change_qty, bom_id, sales_order_line,
                                             sales_order_line.order_id, context=context)
        # 删除又添加相同产品，补回 so_line_id
        change_ids = order_line_cg_obj.search(cr, uid, [('so_id', '=', sales_order_line.order_id.id),
                                                        ('product_id', '=', product_id),
                                                        ('so_line_id', '=', False)], context=context)
        order_line_cg_obj.write(cr, uid, change_ids, {'so_line_id': sales_order_line.id}, context=context)
        return change_message

    def sale_order_change_temporary_operation(self, cr, uid, ids, sale_order_row, location_id, context=None):
        order_line_cg_obj = self.pool.get("sale.order.line.change")
        temporary_row, temporary_table_data = self._get_temporary_table_data(cr, uid, sale_order_row.id, context=context)
        # 检查已采购的变更记录的销售订单行产品是否有变化，如果变化需要清除so_line_id
        so_line_change_ids = order_line_cg_obj.search(cr, uid, [('so_id', '=', sale_order_row.id)], context=context)
        for so_line_change_id in order_line_cg_obj.browse(cr, uid, so_line_change_ids, context=context):
            if so_line_change_id.so_line_id and so_line_change_id.product_id.id != \
                    so_line_change_id.so_line_id.product_id.id:
                so_line_change_id.write({'so_line_id': None})
        return_message = self.judge_order_line_detail(cr, uid, ids, location_id, sale_order_row,
                                                      temporary_table_data, context=context)
        # 处理被采购处理过被删除的产品 新建 订单变化表
        return_message.get('change_message').update(
            self.create_negative_change_line(cr, uid, ids, location_id, sale_order_row, temporary_table_data,
                                             return_message.get('product_ids_spec_map'), context=context))
        temporary_row.unlink() if temporary_row else False
        return return_message

    def create_positive_change_line(self, cr, uid, ids, product_id, location_id, qty_prod_spec, change_qty,
                                    bom_id, sales_order_line, sale_order_row,
                                    context=None):
        if change_qty!=0:
            order_line_cg_obj = self.pool.get('sale.order.line.change')
            order_line_cg_obj.create(cr, uid, {'so_id': ids[
                0], "product_id": product_id, 'stock_location_id': location_id,
                                                        'prod_spec': qty_prod_spec[1],
                                               'stock_picking_type_id': self.get_sale_order_in_picking_type_id(cr, uid,
                                                                                                               ids[0],
                                                                                                               context=context),
                                                        "change_quantity": change_qty,
                                                        'bom_id': bom_id and bom_id[0] or False,
                                                        'so_line_id': sales_order_line.id,
                                                        'expected_delivery_datetime':
                                                            sale_order_row.expected_delivery_time},
                                     context=context)
        return True

    def create_negative_change_line(self, cr, uid, ids, location_id, sale_order_row, temporary_table_data,
                                    product_ids_spec_map, context=None):
        # 处理删除订单行的产品（采购已处理过的）
        change_message = {}
        order_line_obj = self.pool.get('sale.order.line')
        order_line_cg_obj = self.pool.get('sale.order.line.change')
        temporary_table_lists = temporary_table_data.keys()
        temporary_table_list_prod = [
            (table_list, temporary_table_data.get(table_list)[1]) for table_list in temporary_table_lists]
        prodcut_list = [(key, product_ids_spec_map.get(key)[1])
                        for key in product_ids_spec_map.keys()]
        for new_change_product, prod_spec in list((set(temporary_table_list_prod) - set(prodcut_list))):
            if temporary_table_data.get(new_change_product)[0] != 0:
                sale_order_line = order_line_obj.search(cr, uid, [('order_id', '=', ids[0]),
                                                                  ('product_id', '=', new_change_product),
                                                                  ('prod_spec', '=', prod_spec)], context=context)
                change_message.update(
                    {new_change_product: [prod_spec, -temporary_table_data.get(new_change_product)[0]]})
                order_line_cg_obj.create(cr, uid, {'so_id': ids[0], "product_id": new_change_product,
                                                   "change_quantity": -
                                                    temporary_table_data.get(new_change_product)[0],
                                                   'prod_spec': prod_spec, 'stock_location_id': location_id,
                                                    'stock_picking_type_id': self.get_sale_order_in_picking_type_id(cr, uid, ids[0], context=context),
                                                   'so_line_id': sale_order_line and sale_order_line[0],
                                                'expected_delivery_datetime': sale_order_row.expected_delivery_time},
                                         context=context)
        return change_message

    def card_exchange_order_operation(self, cr, uid, ids, sale_order_row, card_exchange_order_id, context=None):
        if sale_order_row.is_exchange and sale_order_row.order_type_id.id == card_exchange_order_id:
            no_apply_delivery_id_sale_order_line = self.pool.get('sale.order.line').search(cr, uid,
                                                    [('order_id', '=', ids[0]), ('card_exchange_id', '=', None)],
                                                    context=context)
            self.pool.get('sale.order.line').write(cr, uid, no_apply_delivery_id_sale_order_line,
                                        {'card_exchange_id': sale_order_row.apply_delivery_id.id}, context=context)
        return True

    def all_raise_error(self, cr, uid, sale_order_row, card_exchange_order_id, return_vals_dict, context=None):
        have_positive_number = return_vals_dict.get('have_positive_number')
        have_negative_number = return_vals_dict.get('have_negative_number')
        is_financial_confirm = return_vals_dict.get('is_financial_confirm')
        user_objs = self.pool.get('res.users').browse(cr, uid, uid, context=context)
        if (sale_order_row.is_exchange or sale_order_row.is_return or
                self.is_need_time_compare(cr, uid, sale_order_row, context=context)) \
        and not self.pool.get('res.users').user_has_groups(cr, uid, groups='purchase.group_purchase_user',context=context) \
        and not self.pool.get('res.users').user_has_groups(cr, uid, groups='dftg_ext.allow_modify_so_expected_date',
                                                        context=context) and not sale_order_row.is_shop_order:
            raise osv.except_osv(
                u'错误', u"此订单需要采购确认，请联系采购!")
        if sale_order_row.state != 'draft':
            raise osv.except_osv(
                u'错误', u"销售订单已经确认，请刷新后再进行操作!")
            # 数据的暂存 以方便后边使用
        # 需求变更
        # if (sale_order_row.is_return or sale_order_row.is_exchang e) and not user_objs.has_group(
        #         'dftg_ext.sale_deputy_manager') \
        #         and not user_objs.has_group('base.group_sale_manager'):
        #     raise osv.except_osv(
        #         u'错误', u"销售退单或者换货单需要销售部门经理或副经理确认!")
        if sale_order_row.is_exchange:
            if not have_positive_number:
                raise osv.except_osv(u'错误', u"换货订单需要有数量为正的产品")
            if not have_negative_number:
                raise osv.except_osv(u'错误', u"换货订单需要有数量为负的产品")
        if sale_order_row.order_type_id.id == card_exchange_order_id:
            if sale_order_row.amount_total != 0:
                raise osv.except_osv(u'错误', u"提货订单的金额需要改为0")
        if is_financial_confirm and not user_objs.has_group('account.group_account_user'):
            raise osv.except_osv(u'错误', u"此订单需要财务确认")

    def action_button_confirm(self, cr, uid, ids, context=None):
        # 确认订单1.对比销售订单临时表和目前表的订单行的数量生成变更历史记录2,继承原来的订单确认的方法。3.删除销售订单临时表
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        sale_order_row = self.browse(cr, uid, ids, context=context)  # 暂时不支持多订单
        location_id = self.get_sale_order_location_id(cr, uid, sale_order_row.id, context=context)[0]
        return_vals_dict = self.sale_order_change_temporary_operation(cr, SUPERUSER_ID, ids, sale_order_row,
                                                                      location_id, context=context)
        self.all_raise_error(cr, uid, sale_order_row, card_exchange_order_id, return_vals_dict, context=context)
        if return_vals_dict.get('change_message'):
            self.change_sale_order_to_notify(cr, uid, sale_order_row.id,
                                             return_vals_dict.get('change_message'), context=context)
        if not sale_order_row.date_order: #为啥再次确认后需要更新确认时间?
            sale_order_row.write({'date_order': datetime.now().strftime(ISODATETIMEFORMAT),
                                  'date_confirm': datetime.now().strftime(ISODATETIMEFORMAT)})
        if return_vals_dict.get('service_order') and not sale_order_row.card_model_id:
            self.action_done(cr, uid, ids, context=context)
            # 暂时取消自动锁定库存
            # self.locked_inventory_operation(cr, uid, ids, context=context)
        # 检查换货单是否同时有正数和负数的产品
        # 如果是提货订单, 把提货订单行的card_exchange_id填上
        self.card_exchange_order_operation(cr, uid, ids, sale_order_row, card_exchange_order_id, context=context)
        return_val = super(sale_order, self).action_button_confirm(cr, uid, ids, context=context)
        is_purchase_user = self.pool.get('res.users').user_has_groups(cr, uid, groups='purchase.group_purchase_user',
                                                                      context=context)
        if is_purchase_user and not sale_order_row.expected_time_confirmed:
            sale_order_row.write({'expected_time_confirmed': True})
        if sale_order_row.state == 'draft':
             self.raise_error(u'订单状态异常，请联系管理员！')
        return return_val
    
    def get_all_warehouse_member(self, cr, uid, context=None):
        employee_obj = self.pool.get('hr.employee')
        employee_ids = employee_obj.search(cr, uid, [('department_id.name', '=', u'仓储部')], context=context)
        user_ids = [employee_row.user_id.id for employee_row in
                    employee_obj.browse(cr, uid, employee_ids, context=context) if employee_row.user_id]
        return user_ids
    
    def change_sale_order_to_notify(self, cr, uid, order_id, change_message, context=None):
        order_row = self.browse(cr, SUPERUSER_ID, order_id, context=context)
        solc_obj = self.pool.get('sale.order.line.change')
        oneday = (datetime.now() + timedelta(days=1)).strftime(ISODATEFORMAT)
        if oneday == order_row.expected_delivery_time and order_row.date_order:
            send_message = '[发货日期变更]明天要发货的订单[%s]进行了修改，请及时校验发货信息！' % (order_row.name)
            self.send_message(cr, uid, self.get_all_warehouse_member(cr, uid, context=context),
                              send_message, context=context)
        if order_row.tally_printer_uid and change_message:
            send_message = u"[销售产品变更]您打印的理货单%s, 有产品变更,请重新打印!"%(order_row.name)
            self.send_message(cr, SUPERUSER_ID, order_row.tally_printer_uid.id, send_message, context=context)
        purchase_product_ids = [change_row.po_id.create_uid.id
                                for change_row in order_row.order_line_change_id \
                                if change_row.po_id and change_row.product_id.id in change_message.keys()
                                and solc_obj.search(cr, uid, [('po_id', '=', False),
                                                              ('so_id', '=', order_id),
                                                              ('product_id', '=', change_row.product_id.id)
                                                              ],
                                                    context=context)
                                ]
        for purchase_uid in list(set(purchase_product_ids)):
            send_message = u'[销售产品变更]您负责采购的产品,在销售订单[%s]中有数量的变更,请及时处理!' % order_row.name
            self.send_message(cr, SUPERUSER_ID, purchase_uid, send_message, context=context)
        return True

    def unlocked_inventory_operation(self, cr, uid, ids, context=None):
        picking_obj = self.pool.get('stock.picking')
        picking_ids = picking_obj.search(cr, uid, [('so_id', '=', ids), ('state', 'not in', ('cancel', 'done'))],
                                         context=context)
        if picking_ids:
            picking_obj.action_cancel(cr, uid, picking_ids, context=context)
        return True

    def apply_change_action(self, cr, uid, ids, context=None):
        # 申请修改，1.修改订单状态2.保存订单中的数据到临时表3.删除未处理的变更历史记录
        sale_order_obj = self.browse(cr, uid, ids, context=context)
        model_data_obj = self.pool.get('ir.model.data')
        #如果是已经草稿了，则直接返回
        if sale_order_obj.state == 'draft':
            return True
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        # 当申请修改时要取消对于原来的 销售订单的出库单的保留..
        if sale_order_obj.order_type_id.id == card_exchange_order_id:
            self.unlocked_inventory_operation(cr, uid, ids, context=context)
            # if not self.pool.get("res.users").has_group(cr, uid, "dftg_ext.unlimited_discount"):
            #     raise osv.except_osv(
            #             u'错误', u"提货订单不能直接，请到提货记录中更改提货信息!")
        sale_order_line_obj = self.pool.get("sale.order.line")
        sale_order_line_changes = self.pool.get("sale.order.line.change")
        open_invoice = True
        sale_order_obj = self.browse(cr, uid, ids, context=context)
        # 申请修改后 要修改 订单行的 状态
        for sale_order_line in sale_order_obj.order_line:
            if not sale_order_line.invoiced and open_invoice:
                open_invoice = False
            sale_order_line_obj.write(cr, uid, sale_order_line.id, {
                                      'state': 'draft'}, context=context)
        if open_invoice:
            raise osv.except_osv(u'错误', u"已经生成系统发票（客户欠款凭证），请联系财务取消系统发票后才能修改订单!")
        no_treatment_order_change_id = sale_order_line_changes.search(
                            cr, uid, [("so_id", "=", ids), ("po_id", "=", False)], context=context)
        sale_order_line_changes.unlink(
                cr, SUPERUSER_ID, no_treatment_order_change_id, context=context)
        self.create_sale_order_temporary(cr, uid, sale_order_obj, context=context)
        self.signal_workflow(cr, uid, ids, 're_draft')
        sale_order_obj.write({'state': 'draft'})  # , 'open_invoice': True
        ###1001 销售订单申请修改按钮点击时需要判断出库单的打印状态，如果出库单已经打印，
        # 则提示：“出库单已经打印，不能申请修改数量，取消出库单请求已经发送给仓库，等待仓库取消后，再申请修改” [Issue开启]
        picking_ids = self.pool.get('stock.picking').search(cr,uid,[('so_id','=',ids),('state','!=','cancel')],context=context)
        for picking in self.pool.get('stock.picking').browse(cr,uid,picking_ids,context=context):
            if picking.print_state:
                raise osv.except_osv(u'错误', u"出库单已经打印，不能申请修改数量\
                                            取消出库单请求已经发送给仓库，等待仓库取消后，再申请修改!")
            # 退货(换货)销售订单申请修改时需要判断是否有未取消的picking，如果存在则提示“请先通知仓库取消退货入库单，才能申请修改” #IMILS
            if sale_order_obj.is_return or sale_order_obj.is_exchange:
                raise osv.except_osv(u'错误', u"请先通知仓库取消退货入库单，才能申请修改!")
        return True
        
    def create_sale_order_temporary(self, cr, uid, sale_order_row, context=None):
        sale_order_line_changes = self.pool.get("sale.order.line.change")
        shipment_order_line = {}
        #变更记录中间表 处理的逻辑的简化 暂时去除
        order_line_change_id = sale_order_line_changes.search(cr, SUPERUSER_ID, [(
            "so_id", "=", sale_order_row.id), ('po_id', '!=', False)], context=context)
        # change表的已处理的数据的暂存, 到中间表,以放便,再次确认时生成变更记录
        for order_line_change_obj in sale_order_line_changes.browse(
                cr, SUPERUSER_ID, order_line_change_id, context=context):
            shipment_order_line.update({order_line_change_obj.product_id.id: [shipment_order_line.get(
                order_line_change_obj.product_id.id, [0, False])[0] + \
        order_line_change_obj.change_quantity, order_line_change_obj.prod_spec]})

        no_treatment_order_change_id = sale_order_line_changes.search(
            cr, uid, [("so_id", "=", sale_order_row.id), ("po_id", "=", False)], context=context)
        sale_order_line_changes.unlink(
            cr, SUPERUSER_ID, no_treatment_order_change_id, context=context)
        self.pool.get("sale.order.temporary").create(cr, SUPERUSER_ID, {'sale_id': sale_order_row.id,
                                                                        "sale_order_dict": str(shipment_order_line)},
                                                     context=context)
        return True

    #
    # def on_change_expected_delivery_time(self, cr, uid, ids, expected_delivery_time, context=None):
    #     # 对期望发货日期加一些判断，如果不和规则就报错.
    #     # if expected_delivery_time < time.strftime(ISODATETIMEFORMAT, time.gmtime(time.time())):
    #     #     return {'value': {'expected_delivery_time': ""},
    #     #             'warning': {'title': u'错误', 'message': u'期望发货日期不能小于等于当前日期'}}
    #
    #     order = self.browse(cr, uid, ids, context=context)
    #     if order:
    #         return {'value': {'currency_id': order[0].pricelist_id.currency_id.id}}
    def change_expected_delivery_time(self, cr, uid, sale_order_row, vals, context=None):
        if vals.get("expected_delivery_time") or vals.get('shipping_type_sale'):
            if sale_order_row.state not in ('cancel', 'draft') and \
                    (self.is_need_time_compare(cr, uid, sale_order_row, context=context)):
                self.done_change_delivery_time_send_message(cr, uid, sale_order_row, vals, context=context)
                if vals.get("expected_delivery_time"):
                    sale_order_row.write({'expected_time_confirmed': False})
            elif not self.is_need_time_compare(cr, uid, sale_order_row, context=context):
                sale_order_row.write({'expected_time_confirmed': True})
            for order_line_change in sale_order_row.order_line_change_id:
                order_line_change.write({'expected_delivery_datetime': vals.get("expected_delivery_time")})
            # if context.get('warehouse_user'):
            #     create_date_time = time.strptime(vals.get("expected_delivery_time"), ISODATEFORMAT)
            #     date_create_order = datetime(*create_date_time[:3], tzinfo=pytz.timezone("Asia/Shanghai"))
            #     if sale_order_row.date_order or (sale_order_row.date_order.split(' ')[0]) >\
            #             (date_create_order + timedelta(days=-2)).strftime(ISODATEFORMAT):
            #         raise osv.except_osv(u'错误', u"只允许仓库修改发货日期为订单确认日期加上2天后的日期!")
            # 修改期望发货日期发送消息给对应的采购员
            self.change_delivery_time_send_message(cr, uid, sale_order_row, vals, context=context)
        if vals.get("expected_delivery_time") or vals.get("expected_delivery_hour"):
            self.check_sale_expect_delivery_time(cr, uid, 'write', sale_order_row.expected_delivery_time,
                                                 sale_order_row.expected_delivery_hour, context=context)
    
    def write(self, cr, uid, ids, vals, context=None):
        # 修改时的判断1.已发货的产品不允许删除2.判断订单类型与订单的类型是否符合（销售订单、换货单、退货单）
        # 1.如果此销售订单已有发票或者出入库单，则不允许修改订单的 客户
        # 2.如果销售订单上的产品已经发货则不允许在销售订单上删除次产品
        # 3.销售订单的类型不允许进行变化，一旦新建完成后销售订单的类型就确定不能修改
        sale_order_lines = self.pool.get("sale.order.line")
        product_ids = []
        if vals.get('order_line', False):
            for order_line_change in vals.get('order_line'):
                if order_line_change[0] == 2:
                    sale_order_line_obj = sale_order_lines.browse(
                        cr, uid, order_line_change[1], context=context)
                    if sale_order_line_obj and sale_order_line_obj.delivered_qty > 0:
                        raise osv.except_osv(u'错误', u"此产品已发货，不允许删除")
                    if sale_order_line_obj and sale_order_line_obj.invoiced:
                        raise osv.except_osv(u'错误', u"此产品已开票，不允许删除")
                ## 记录商城订单修改的产品的id 。 在后面进行检查，避免非商城订单 添加商城订单专属产品
                if order_line_change[0] == 1 or order_line_change[0] == 0:
                    if 'product_id' in order_line_change[2]:
                        product_ids.append(order_line_change[2].get('product_id'))
        for sale_order_row in self.browse(cr, uid, ids, context=context):
            oneday = (datetime.now() + timedelta(days=1)).strftime(ISODATEFORMAT)
            if vals.get("expected_delivery_time") and oneday == sale_order_row.expected_delivery_time:
                send_message = '明天要发货的订单[%s]，发货日期修改为%s'% (sale_order_row.name,
                                vals.get("expected_delivery_time", False) or sale_order_row.expected_delivery_time)
                self.send_message(cr, uid, self.get_all_warehouse_member(cr, uid, context=context),
                                  send_message, context=context)
        super(sale_order, self).write(cr, uid, ids, vals, context=context)
        for sale_order_row in self.browse(cr, uid, ids, context=context):
            if vals.get("order_line"):
                context = dict({'product_ids': product_ids}, **context)
                self.sale_order_product_no_repeat(cr, uid, sale_order_row, context=context)
            if vals.get("partner_id"):
                if not sale_order_row.real_so_id:
                    for invoice in sale_order_row.invoice_ids:
                        if invoice.state != 'cancel':
                            raise osv.except_osv(u'错误', u"此订单已经生成发票,请取消发票后再进行操作!")
                for stock_picking in self.pool.get('stock.picking').browse(cr, uid,
                                    self.pool.get('stock.picking').search(cr,uid, [('so_id','=',sale_order_row.id)],
                                                    context=context),context=context):
                    if stock_picking.state != 'cancel':
                        raise osv.except_osv(u'错误', u"此订单已经生成出/入库单据,请取消单据后再进行操作!")
            self.is_gift_order_type(cr, uid, sale_order_row, context=context)
            self.change_expected_delivery_time(cr, uid, sale_order_row, vals, context=context)
            self.change_picking_address(cr, uid, sale_order_row, vals, context=context)
        return True

    def is_gift_order_type(self, cr, uid, sale_order_row, context=None):
        if sale_order_row.is_gift_order:
            self.pool.get('sale.order.line').write(cr, uid, [line.id for line in sale_order_row.order_line],
                                                   {'price_unit': 0}, context=context)

    def change_picking_address(self, cr, uid, sale_order_row, vals, context=None):
        # 销售订单地址相关信息调整， 则同步修改销售订单对应的出入库单的地址信息
        picking_obj = self.pool.get("stock.picking")
        addres_fields = ['street', 'city_id', 'county_id', 'province_id', 'consignee', 'zip', 'mobile_number', 'phone_number']
        change_address_field = list(set(vals.keys()).intersection(set(addres_fields)))
        change_address_dict = {field: vals.get(field) for field in change_address_field}
        picking_ids = picking_obj.search(cr, uid, [('so_id', '=', sale_order_row.id)], context=context)
        if sale_order_row.express_delivery_ids and change_address_dict:
            raise osv.except_osv(u'错误', u"此订单为一单多发的情况，不能修改地址!")
        if change_address_dict and picking_ids:
            picking_obj.write(cr, uid, picking_ids, change_address_dict, context=context)
        return True

    def change_delivery_time_send_message(self, cr, uid, sale_row, vals, context=None):
        purchase_ids = [change_row.po_id.id for change_row
                                 in sale_row.order_line_change_id if change_row.po_id]
        if vals.get('expected_delivery_time') and (sale_row.shipping_type_sale == 'direct_delivery' or
                                        vals.get('shipping_type_sale') == 'direct_delivery') and purchase_ids:
            send_message = "[期望发货日期]您负责的采购的直送销售订单%s,送货日期修改到%s!\n请及时处理" % (sale_row.name,
                                                                          vals.get('expected_delivery_time'))
            send_user_list = []
            for purchase_row in self.pool.get('purchase.order').browse(cr, SUPERUSER_ID, purchase_ids, context=context):
                purchaser_id = purchase_row.create_uid.id
                if purchaser_id not in send_user_list:
                    send_user_list.append(purchaser_id)
                    self.send_message(cr, uid, purchaser_id, send_message, context=context)
        if vals.get('expected_delivery_time') and sale_row.is_tallying_printed and sale_row.tally_printer_uid:
            send_message = "[期望发货日期]您打印的理货单%s[%s],送货日期修改到%s!\n请及时处理" % (sale_row.name,
                                                sale_row.partner_id.name, vals.get('expected_delivery_time'))
            self.send_message(cr, uid, sale_row.tally_printer_uid.id, send_message, context=context)
        return True

    def done_change_delivery_time_send_message(self, cr, uid, sale_row, vals, context=None):
        purchase_ids = [change_row.po_id.id for change_row
                                 in sale_row.order_line_change_id if change_row.po_id]
        send_message = "[期望发货日期]您负责的采购的销售订单%s,送货日期修改到%s!\n请及时处理" % (sale_row.name,
                                                                      vals.get('expected_delivery_time'))
        send_user_list = []
        for purchase_row in self.pool.get('purchase.order').browse(cr, SUPERUSER_ID, purchase_ids, context=context):
            purchaser_id = purchase_row.create_uid.id
            if purchaser_id not in send_user_list:
                send_user_list.append(purchaser_id)
                self.send_message(cr, uid, purchaser_id, send_message, context=context)
        return True

    def sale_order_product_no_repeat(self,cr,uid,sale_order_obj,context=None):
        if not sale_order_obj.real_so_id:
            is_return = sale_order_obj.is_return
            is_exchange = sale_order_obj.is_exchange
            product_ids = []
            is_add, is_minus, is_old_shop_order = False, False, False
            for invoice in sale_order_obj.invoice_ids:
                if invoice.account_id.code == '2205':
                    is_old_shop_order = True
            for order_line in sale_order_obj.order_line:
                if order_line.product_id.id in context.get('product_ids', []) and \
                        not sale_order_obj.is_shop_order and u'商城' in order_line.product_id.name and \
                        not is_old_shop_order:
                    raise osv.except_osv(u'错误', u"如果需要下商城订单，请打勾'是否商城订单'!")
                if not order_line.product_id.categ_id.property_account_income_categ and not is_old_shop_order:
                    raise osv.except_osv(u'错误', u"产品分类上获取不到对应的收入科目!")
                if sale_order_obj.is_shop_order and order_line.product_id.categ_id.property_account_income_categ.name \
                        != u'长期应付款':
                    raise osv.except_osv(u'错误', u"产品分类设置不对，请检查分录的科目是否为长期应付款!")
                # 统一订单中不允许存在 产品种类和数量的正负 一样的订单行
                product_ids.append('%s%s' % (
                    order_line.product_id.id, False if order_line.product_uom_qty > 0 else True))
                if is_return and order_line.product_uom_qty > 0:
                    raise osv.except_osv(u'错误', u"退货单中产品数量不能为正!")
                if order_line.product_uom_qty > 0 and not is_add:
                    is_add = True
                if order_line.product_uom_qty < 0 and not is_minus:
                    is_minus = True
                self.check_product_is_expense(cr, uid, order_line, context=context)
            if (not (is_minus and is_add)) and is_exchange:
                raise osv.except_osv(u'错误', u"订单修改不正确，换货单中产品数量必须有正有负!")
            if (not is_minus or is_add) and is_return:
                raise osv.except_osv(u'错误', u"订单修改不正确，退货单中必须只有负数的产品!")
            if not (is_return or is_exchange) and is_minus:
                raise osv.except_osv(u'错误', u"订单修改不正确，正常销售订单中不能有负数的产品!")
            if len(product_ids) != len(list(set(product_ids))):
                raise osv.except_osv(u'错误', u"同一订单中，相同产品不允许有不同的订单行!")
        return True

    def check_sale_expect_delivery_time(self, cr, uid, create_or_write ,
                                        expected_delivery_time, expected_delivery_hour, context=None):
        need_judge_hour_tuple = (u'9点~10点', u'10点~11点', u'11点~12点', u'12点~13点',
         u'13点~14点', u'14点~15点', u'16点~17点', u'17点~18点')
        if expected_delivery_time and expected_delivery_hour and expected_delivery_hour in need_judge_hour_tuple:
            (u'9点~10点', u'10点~11点', u'11点~12点', u'12点~13点',
             u'13点~14点', u'14点~15点' , u'16点~17点',u'17点~18点')
            sale_ids = self.search(cr, uid, [('state', 'not in', ('draft', 'cancel')),
                                             ('expected_delivery_time', '=', expected_delivery_time),
                                             ('expected_delivery_hour', '=', expected_delivery_hour)
                                            ], context=context)
            if create_or_write =='create' and sale_ids or \
                                    create_or_write =='write' and len(sale_ids) > 1:
                sale_row = self.browse(cr, uid, sale_ids[0], context=context)
                self.raise_error("销售订单%s 已选择在%s %s发货！\n请选择其他时间段发货或者与冲突销售订单协商！"%
                                 (sale_row.name, expected_delivery_time, expected_delivery_hour))
        
        return True

    def update_set_order_type_id(self, cr, uid, vals, order_type_id, context=None):
        model_data_obj = self.pool.get('ir.model.data')
        if vals.get('is_card'):
            order_type_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_order')
        elif vals.get('is_shop_order'):
            order_type_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_shop_order')
        elif vals.get('is_gift_order'):
            order_type_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_customer_gift_order')
        elif not order_type_id:
            order_type_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_normal_order')
        return order_type_id

    def create(self, cr, uid, vals, context=None):
        # 1销售订单类型的确定（销售订单、换货单、退货单）
        # 2.同一种产品（同prepare_vals一种产品正负不一样可以存在）不允许出现两次
        # 3.加入产品的型号的字段的判断
        model_data_obj = self.pool.get('ir.model.data')
        order_type_id = self.update_set_order_type_id(cr, uid, vals,
                                                      vals.get('order_type_id'), context=context)
        vals.update({'order_type_id': order_type_id}) if order_type_id else False
        if vals.get('is_sample'):
            vals.update({'name': self.pool.get('ir.sequence').get(cr, uid, 'sample_order') or '/'})
        sale_order_id = super(sale_order, self).create(
            cr, uid, vals, context=context)
        sale_order_ob=self.browse(cr,uid,sale_order_id,context=context)
        #写入share_to_uid
        sale_order_ob.write({'share_to_uid': sale_order_ob.partner_id.share_to_uid and \
                                             sale_order_ob.partner_id.share_to_uid.id or vals.get("user_id") != uid and uid})
        #写入客户级别
        sale_order_ob.write({'cust_grade': sale_order_ob.partner_id.cust_grade})
        #取得订单行
        order = self.browse(cr, uid, sale_order_id, context=context)[0]
        self.is_gift_order_type(cr, uid, order, context=context)
        if not vals.get('order_line'):
            raise osv.except_osv(u'错误', u"订单行不能为空!")
        if vals.get("expected_delivery_time"):
            for order_line_change in order.order_line_change_id:
                order_line_change.write({'expected_delivery_datetime': vals.get("expected_delivery_time")})
        if not vals.get("real_so_id"):
            sale_orders = self.browse(cr, uid, sale_order_id, context=context)
            is_add = False
            is_minus = False
            product_ids = []
            card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
            for order_line in sale_orders.order_line:
                if not sale_orders.is_shop_order and u'商城' in order_line.product_id.name:
                    raise osv.except_osv(u'错误', u"如果需要下商城订单，请打勾'是否商城订单'!")
                if not order_line.product_id.categ_id.property_account_income_categ:
                    raise osv.except_osv(u'错误', u"产品分类上获取不到对应的收入科目!")
                if sale_orders.is_shop_order and order_line.product_id.categ_id.property_account_income_categ.name \
                        != u'长期应付款':
                    raise osv.except_osv(u'错误', u"产品分类设置不对，请检查分录的科目是否为长期应付款!")
                product_ids.append('%s,%s,%s' % (
                    order_line.product_id.id, False if order_line.product_uom_qty > 0 else True,order_line.product_id.default_code))
                if order_line.product_uom_qty > 0 and not is_add:
                    is_add = True
                if order_line.product_uom_qty < 0 and not is_minus:
                    is_minus = True
                self.check_product_is_expense(cr, uid,  order_line, context=context)
            if len(product_ids) != len(list(set(product_ids))):
                product_list2=[product for product in product_ids if product_ids.count(product)>1]
                raise osv.except_osv(u'错误', u"产品(编号%s)重复了!"%(product_list2[0].split(',')[2]))
            if is_minus and not is_add:
                if not order.apply_delivery_id:
                    raise osv.except_osv(u'错误', u"请在原订单中的更多下拉里选择生成退货单！")
                self.write(cr, uid, sale_order_id, {
                           "is_return": True}, context=context)
            # 如果数量有正有负
            elif is_add and is_minus:
                self.write(cr, uid, sale_order_id, {"is_exchange": True}, context=context)
            # 如果是复制提货订单, 那么要强制变成提货换货订单
            elif hasattr(request, 'params') and request.params.get('method') == 'copy'\
                    and vals.get('order_type_id') == card_exchange_order_id:
                #提货记录写入换货销售订单id
                apply_delivery_id = order.apply_delivery_id
                if apply_delivery_id:
                    #写入提货记录的换货订单id
                    apply_delivery_id.write({'exchange_so_id': sale_order_id})

                #写入换货单标记
                self.write(cr, uid, sale_order_id, {"is_exchange": True}, context=context)
            if vals.get("expected_delivery_time") or vals.get("expected_delivery_hour"):
                self.check_sale_expect_delivery_time(cr, uid, 'create',vals.get("expected_delivery_time"),
                                                     vals.get("expected_delivery_hour"), context=context)
        return sale_order_id

    def check_product_is_expense(self, cr, uid, order_line, context=None):
        model_data_obj = self.pool.get('ir.model.data')
        expense_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'hr_expense.cat_expense')
        if order_line.product_id.categ_id.id == expense_id:
            raise osv.except_osv(u'错误', u"费用类产品(%s)不能下单，只用于报销！"%(order_line.product_id.name))
        return True

     # 调用出库助手向导
    def out_going_confirm(self, cr, uid, ids, context=None):
        # 出库助手wizard的调用并传递sale_id
        sale_order_id = ids[0]
        sale_order_obj = self.pool.get("sale.order").browse(cr, uid, sale_order_id, context=context)
        product_id_order_dcit = {}
        if sale_order_obj.express_delivery_ids and context.get('warehouse_user'):
            raise osv.except_osv(u'错误！', u'导入过一单多发快递地址，请到快递发货那里操作!')
        for order_line in sale_order_obj.order_line:
            if order_line.delivery_quantity_qty != 0 and order_line.product_id.type != 'service':
                product_id_order_dcit.update({order_line.product_id.id: order_line})
            # if order_line.locked_quant_qty !=0 and\
            #                 order_line.locked_quant_qty != order_line.delivery_quantity_qty:
            #     raise osv.except_osv(_('Warning!'), u'锁定库存的产品(%s)不允许分批发货!'%(order_line.product_id.name))
        if not product_id_order_dcit:
            raise osv.except_osv(_('Warning!'), u'本次没有填写要发货的数量,请核对后再进行操作!')
        return {
            'type': 'ir.actions.act_window',
            'res_model': "confirm.out.going.wizard",
            'view_type': 'form',
            'view_mode': 'form',
            'target': 'new',
            'context': {'default_sale_order_id': sale_order_id},
        }

    def onchange_common_address(self, cr, uid, ids, common_address, note=False, context=None):
        # 默认联系人
        address = self.pool.get('customer.address').browse(
            cr, uid, common_address, context=context)
        if address:
            return {'value': {
                'province_id': address.province_id.id, 'city_id': address.city_id.id, 'county_id': address.county_id.id,
                'phone_number': address.phone_number, 'mobile_number': address.mobile_number, 'street': address.street,
                'zip': address.zip, 'consignee': address.contacts
            }}
        else:
            return {'value': {
                'province_id': '', 'city_id': '', 'county_id': '', 'phone_number': '', 'mobile_number': '', 'street': '',
                'zip': '', 'consignee': ''
            }}

    def onchange_contacts(self, cr, uid, ids, contacts_id, note=False, context=None):
        # 去除掉customer_address，替换成company_contacts
        contacts = self.pool.get('company.contacts').browse(
            cr, uid, contacts_id, context=context)
        if contacts:
            partner = contacts.contacts_id
            return {'value': {
                'province_id': partner.province_id.id, 'city_id': partner.city_id.id, 'county_id': partner.county_id.id,
                'phone_number': partner.phone, 'mobile_number': partner.mobile, 'street': partner.street,
                'zip': partner.zip, 'consignee': partner.name, 'zip': partner.zip
            }}

        return {'value': {
                'province_id': '', 'city_id': '', 'county_id': '',
                'phone_number': '', 'mobile_number': '', 'street': '',
                'zip': '', 'consignee': '', 'zip': ""}}

    def onchange_partner_id(self, cr, uid, ids, partner_id, context=None):
        # 去除掉customer_address，替换成company_contacts
        returns = super(sale_order, self).onchange_partner_id(cr, uid, ids,
                                                              partner_id, context=context)
        if partner_id:
            contacts_id = self.pool.get('company.contacts').search(cr, uid, [(
                'partner_id', '=', partner_id), ('is_main_contacts', '=', True)], context=context)
            if contacts_id:
                returns.get('value').update(
                    {'contacts_id': contacts_id[0]})
            res_partner_obj = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
            returns.get('value').update({'partner_name': res_partner_obj.name,
                                         'payment_method':res_partner_obj.payment_method})
            # 根据多联系人的地址更新常用收货地址的值
            address_id = self.pool.get('customer.address').search(cr, uid, [(
                'partner_id', '=', partner_id), ('default', '=', True)], context=context)
            if address_id:
                returns.get('value').update({'common_address': address_id[0]})

        return returns

    def view_out_outgoing_incoming(self, cr, uid, ids, context=None):
        # sale_order_objs = self.browse(cr, uid, ids, context=context)
        # stock_picking_id = self.pool.get('stock.picking').search(
        # cr, uid, [('origin', '=', sale_order_objs.name)], context=context)
        # 查看出入库单
        model_data_obj = self.pool.get('ir.model.data')
        stock_picking_tree_ids = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'dftg_stock_picking_tree')
        view_picking_form_id = model_data_obj.xmlid_to_res_id(
            cr, SUPERUSER_ID, 'stock.view_picking_form')
        return {
            'type': 'ir.actions.act_window',
            'res_model': 'stock.picking',
            'view_mode': 'tree,form',
            'view_type': 'form',
            'name': u'查看出/入库单',
            'views': [(stock_picking_tree_ids, 'tree'), (view_picking_form_id, 'form')],
            'view_id': stock_picking_tree_ids,
            'domain': "[('so_id','='," + str(ids[0]) + ")]",
            'context':{'no_change':True},
        }

    def _get_all_invoiced_order(self, cr, uid, ids, context=None):
        order_id = {}
        for invoice in self.pool.get('account.invoice').browse(cr, uid, ids, context=context):
            if invoice.state == 'open' or invoice.state == 'paid':
                for order in self.pool.get("sale.order").search(cr, uid, [('invoice_ids', 'in', invoice.id)], context=context):
                    order_id[order] = True
        return order_id.keys()

    def _get_reconciled_info(self, cr, uid, ids, name, arg, context=None):
        # 统计已经付款的金额（本张销售订单）
        res = {}
        if not ids:
            return res
        for order in self.browse(cr, uid, ids, context=context):
            total_num = 0
            for invoice in order.invoice_ids:
                total_num += invoice.residual
            if order.amount_total > 0:
                res[order.id] = order.amount_total - total_num
            else:
                res[order.id] = order.amount_total + total_num
        return res

    def _get_invoiced_info(self, cr, uid, ids, name, arg, context=None):
        res = {}
        # 统计已开发
        if not ids:
            return res
        for order in self.browse(cr, uid, ids, context=context):
            total_num = 0
            for invoice in order.invoice_ids:
                if invoice.state != 'cancel':
                    total_num += invoice.amount_total
            res[order.id] = total_num
        return res

    def card_order_create_account_move(self, cr, uid, order_id, context=None):
        model_data_obj = self.pool.get('ir.model.data')
        account_obj = self.pool.get('account.account')
        move_obj = self.pool.get('account.move')
        order_type_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID,
                                                       'dftg_ext.dftg_order_type_data_card_exchange_order')
        prepay_journal_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.dftg_prepay_journal')
        account_id = account_obj.search(cr, uid, [('name', '=', u'预收账款')], context=context)
        account_in_id = account_obj.search(cr, uid, [('name', '=', u'主营业务收入')], context=context)
        account_out_id = account_obj.search(cr, uid, [('name', '=', u'长期应付款')], context=context)
        for order in self.browse(cr, uid, order_id, context=context):
            if order.order_type_id.id != order_type_id and account_in_id and account_out_id and account_id:
                continue
            account_id = account_id and account_id[0] or account_id
            cr.execute("""SELECT ai.amount_total AS amount, card_so.card_qty AS qty,card_so.id AS card_so,
                              ai.id AS invoice, card_so.partner_id AS partner_id
                              FROM account_invoice ai
                              INNER JOIN sale_order so ON so.name=ai.origin
                              INNER JOIN apply_delivery ap ON ap.card_so_id=so.id AND ap.orign_apply_delivery_id IS NULL 
                              INNER JOIN sale_order card_so ON card_so.id=ap.card_so_id
                              WHERE ai.state!='cancel' AND ai.account_id=%s AND ap.so_id=%s;""" % (account_id, order.id))
            records = cr.dictfetchone()
            if not records:
                continue
            amount = records.get('amount') / records.get('qty')
            move_vals = {'journal_id': prepay_journal_id, 'ref': order.name,
                         'line_id': [(0, 0, {'name': u'卡券提货产品费', 'account_id': account_out_id and account_out_id[0],
                                             'partner_id': records.get('partner_id'),
                                             'debit': amount}),
                                     (0, 0, {'name': u'卡券提货产品费', 'account_id': account_in_id and account_in_id[0],
                                             'partner_id': records.get('partner_id'),
                                             'credit': amount})]}
            move_obj.create(cr, uid, move_vals, context=context)
        return True

    def _compute_shipment_completion(self, cr, uid, ids, name, arg, context=None):
        # 计算字段，当满足要求的时候就执行工作流的一些操作，还有
        res = {}
        if not ids:
            return res
        line_obj = self.pool.get('sale.order.line')
        for order in self.browse(cr, uid, ids, context=context):
            # total_num来判断是否 已经发货完成

            total_num = True
            for order_line in order.order_line:
                if order_line.product_id.type!='service' and total_num and (order_line.shipped_qty != abs(order_line.product_uom_qty) or order_line.delivered_qty != abs(order_line.product_uom_qty)):
                    total_num = False
            if total_num:
                picking_id = self.pool.get('stock.picking').search(
                    cr, uid, [('so_id', '=', order.id), ('state', '!=', 'cancel')], context=context)
                if picking_id and not all([picking_obj.state == 'done' for picking_obj in self.pool.get('stock.picking').browse(cr, uid, picking_id, context=context)]):
                    total_num = False
            if total_num:
                # context.update['active_id'] = order.id
                invoice_dict = self.pool.get('sale.order').manual_invoice(
                    cr, SUPERUSER_ID, [order.id], context=context)  # 会触发流程上的操作
                # 发货完成后要生成发票。（除了样品订单, 目前样品订单发货后不会变成已发货状态，也就不会生成发票)
                if not order.is_sample:
                    invoce_obj = self.pool.get('account.invoice').browse(cr, SUPERUSER_ID, [invoice_dict.get('res_id')], context=context)
                    invoce_obj.signal_workflow('invoice_open')
                # self.pool.get('sale.order').action_invoice_create(
                #     cr, uid, order.id, context=context) 生成发票 不会触发流程上的操作
                # 根据总价格 和 计算此订单的 毛利提成类型
                date_order = order.date_order and order.date_order[0:10]
                line_obj.gross_profit_rate_type(cr, uid, [line.id for line in order.order_line], context=context)
            res[order.id] = total_num
        return res

    # def create_invoice_action_simulation(self, cr, uid, ids, context=None): #
    # 仿照系统开票的流程进行开票-试验, 结果失败 context 在function字段下 active_id 和active_ids 不能更新 另外这条wizard 的记录没有和销售订单进行强关联
    # 只有active_id 另外 这样返回的action 也会和手动操作有区别
    #     result = {}
    #     res = {}
    #     mod_obj = self.pool.get('ir.model.data')
    #     act_obj = self.pool.get('ir.actions.act_window')
    #     result = mod_obj.get_object_reference(
    #         cr, uid, 'sale', 'action_view_sale_advance_payment_inv')
    #     id = result and result[1] or False
    #     result = act_obj.read(cr, uid, [id], context=context)[0]
    #     res = mod_obj.get_object_reference(
    #         cr, uid, 'sale', 'view_sale_advance_payment_inv')
    #     result['views'] = [(res and res[1] or False, 'form')]
    #     return result

    def _determine_shipment_completion(self, cr, uid, ids, context=None):
        res = {}
        if not ids:
            return res
        for order_line in self.pool.get('sale.order.line').browse(cr, uid, ids, context=context):
            if (order_line.shipped_qty == order_line.product_uom_qty and order_line.delivered_qty == order_line.product_uom_qty) or (
                    order_line.product_uom_qty < 0 and order_line.shipped_qty == abs(order_line.product_uom_qty)) or order_line.state=='done':
                res[order_line.order_id.id] = True
        return res.keys()
    
    def action_reverse_done(self, cr, uid, ids, context=None):
        for order in self.browse(cr, uid, ids, context=context):
            self.pool.get('sale.order.line').write(cr, uid,
                                                   [line.id for line in order.order_line if line.state != 'cancel'],
                                                   {'state': 'exception'}, context=context)
        return self.write(cr, uid, ids, {'state': 'invoice_except'}, context=context)

    def _invoiced(self, cursor, user, ids, name, arg, context=None):
        res = {}
        for sale in self.browse(cursor, user, ids, context=context):
            res[sale.id] = True
            invoice_existence = False
            for invoice in sale.invoice_ids:
                if invoice.state != 'cancel':
                    invoice_existence = True
                    if invoice.state != 'paid':
                        res[sale.id] = False
                        break
            if not invoice_existence or sale.state == 'manual':
                res[sale.id] = False
            if sale.amount_total == 0:
                res[sale.id] = True
            if sale.shipment_completion and res[sale.id] and sale.state != "done":
                self.pool.get("sale.order").action_done(cursor, user, [sale.id], context=context)
            if sale.state == "done" and not res[sale.id]:
                self.pool.get("sale.order").action_reverse_done(cursor, user, [sale.id], context=context)
        return res

    def re_create_invoice(self, cr, uid, ids, context=None):
        self.pool.get('sale.order').action_invoice_create(cr, uid, ids, context=context)
        return True

    def _get_moving_picking(self, cr, uid, ids, context=None):
        # 找到所有的由本张销售订单生成的出入库单
        res = {}
        if not ids:
            return res
        for picking in self.pool.get('stock.picking').browse(cr, uid, ids, context=context):
            if picking.state in ['done', 'cancel'] and picking.so_id:
                res[picking.so_id.id] = True
        return res.keys()

    def _invoiced_search(self, cursor, user, obj, name, args, context=None):
        if not len(args):
            return []
        clause = ''
        sale_clause = ''
        no_invoiced = False
        for arg in args:
            if (arg[1] == '=' and arg[2]) or (arg[1] == '!=' and not arg[2]):
                clause += 'AND inv.state = \'paid\''
            else:
                clause += 'AND inv.state != \'cancel\' AND sale.state != \'cancel\'  AND inv.state <> \'paid\'  AND rel.order_id = sale.id '
                sale_clause = ',  sale_order AS sale '
                no_invoiced = True

        cursor.execute('SELECT rel.order_id '
                       'FROM sale_order_invoice_rel AS rel, account_invoice AS inv ' + sale_clause +
                       'WHERE rel.invoice_id = inv.id ' + clause)
        res = cursor.fetchall()
        if no_invoiced:
            cursor.execute('SELECT sale.id '
                           'FROM sale_order AS sale '
                           'WHERE sale.amount_total <> 0 AND sale.id NOT IN '
                           '(SELECT rel.order_id '
                           'FROM sale_order_invoice_rel AS rel) and sale.state != \'cancel\'')
            res.extend(cursor.fetchall())
        if not res:
            return [('id', '=', 0)]
        return [('id', 'in', [x[0] for x in res])]

    def _compute_gross_profit_total(self, cr, uid, ids, name, arg, context=None):
        # 计算出本张订单 行的毛利
        res = {}
        for sale_order in self.browse(cr, uid, ids, context=context):
            gross_profit_total = 0
            for sale_order_line in sale_order.order_line:
                gross_profit_total = sale_order_line.gross_profit + gross_profit_total

            res[sale_order.id] = gross_profit_total
        return res

    def _compute_is_reconciled(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for sale_order in self.browse(cr, uid, ids, context=context):
            if sale_order.reconciled_num == sale_order.amount_total:
                res[sale_order.id] = True
            else:
                res[sale_order.id] = False
        return res

    #取得销售订单的所有出库单的发货人
    def _compute_delivery_user(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for so_id in ids:
            delivery_users = []
            picking_ids = self.pool.get('stock.picking').search(cr, uid, [('so_id', '=', so_id)], context=context)
            for picking in self.pool.get('stock.picking').browse(cr, uid, picking_ids, context=context):
                if picking.delivery_user_id:
                    delivery_users.append(picking.delivery_user_id.name)
            res[so_id] = ",".join(delivery_users)
        return res

    def _get_sale_order_line_invoice_num(self, cr, uid, ids, context=None):
        res = {}
        for sol in self.pool.get('sale.order.line').browse(cr, uid, ids, context=context):
            if sol.invoiced:
                res[sol.order_id.id] = True
        return res

    def _compute_purchase_order_managerment(self, cr, uid, ids,name, arg, context=None):
        res = {}
        for sale in self.browse(cr ,uid, ids, context=context):
            if sale.order_line_change_id:
                res[sale.id] = any([change.po_id and change.po_id.id for change in sale.order_line_change_id])
            else:
                res[sale.id] = True
        return res

    def _get_purchase_order_managerment(self, cr, uid, ids, context=None):
        res = {}
        for so_lcg in self.pool.get('sale.order.line.change').browse(cr, uid, ids, context=context):
            res[so_lcg.so_id.id] = True
        return res

    def onchange_is_sample(self, cr, uid, ids, is_sample, context=None):
        # 如果选中是样品，则送货方式默认为销售员
        return_values = {'value': {}}
        if is_sample:
            model_data_obj = self.pool.get('ir.model.data')
            sample_order_type_id = model_data_obj.xmlid_to_res_id(
                cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_sample_order')
            return_values['value'].update({'shipping_type_sale': 'sale_delivery', 'order_type_id': sample_order_type_id})
        return return_values

    def is_need_time_compare(self, cr, uid, sale_order_row, context=None):
        # 如果是退货订单或者卡券订单，则必须审核
        if sale_order_row.state is 'draft' and (sale_order_row.is_return or sale_order_row.is_card):
            return True
        # 提货订单不用审核
        if sale_order_row.apply_delivery_id:
            return False

        # 如果假单则不用比较发货日期
        if sale_order_row.real_so_id:
            return False

        # 如果是中秋订单则不用比较发货日期 zhongqiu_product 设定值进行设定中秋产品是否可以保存。
        compare_time = True
        parameter_obj = self.pool.get('ir.config_parameter')
        model_data_obj = self.pool.get('ir.model.data')
        card_exchange_order_id = model_data_obj.xmlid_to_res_id(
             cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        parameter_id = parameter_obj.search(cr, uid, [('key', '=', 'dftg.zhongqiu_product')], context=context)[0]
        parameter_row = parameter_obj.browse(cr, uid, parameter_id, context=context)
        if parameter_row.value == '1':
            for line in sale_order_row.order_line:
                seanson_list = [season.season for season in line.product_id.product_tmpl_id.season_ids]
                if len(seanson_list) != len(list(set(seanson_list) - set(['zhongqiu']))):
                    compare_time = False
        # 提货订单不用比
        if sale_order_row.order_type_id.id == card_exchange_order_id:
            compare_time = False
        if compare_time:
            return_vals = self.time_compare(cr, uid, sale_order_row.create_date,
                                            sale_order_row.expected_delivery_time, context)
        else:
            return_vals = False
        return return_vals

    def judge_is_need_purchase_audit(self, cr, uid, ids, name, args, context=None):
        res = {}
        for sale_order_row in self.browse(cr, uid, ids, context=context):
            if sale_order_row.order_type_id.order_type_code == 'normal_order' and not sale_order_row.real_so_id:
                if sale_order_row.state in ('draft', 'cancel'):
                    res[sale_order_row.id] = self.is_need_time_compare(cr, uid, sale_order_row, context=context)
                else:
                    res[sale_order_row.id] = not sale_order_row.expected_time_confirmed
            else:
                res[sale_order_row.id] = False
        return res

    def compute_is_gross_profit_checked(self, cr, uid, ids, name, args, context=None):
        res = {}
        for order in self.browse(cr, uid, ids, context=context):
            res[order.id] =order.order_line and all([line.is_gross_profit_checked for line in order.order_line])
        return res

    def sale_order_line_is_gross_profit_checked(self, cr, uid, ids, context=None):
        order_ids = []
        for line in self.pool.get('sale.order.line').browse(cr, uid, ids, context=context):
            order_ids.append(line.order_id.id)
        return order_ids

    def _compute_quant_is_reservate(self, cr, uid, ids, name, args, context=None):
        res = {}
        for order_row in self.browse(cr, SUPERUSER_ID, ids, context=context):
            quant_ids = self.pool.get('stock.quant').search(cr, SUPERUSER_ID, [('reservation_id', '=', -1),
                                                          ('so_id', '=', order_row.id)], context=context)
            if quant_ids:
                res[order_row.id] = True
            else:
                res[order_row.id] = False
        return res

    def _get_locked_quant(self, cr, uid, ids, context=None):
        so_ids = []
        move_obj = self.pool.get('stock.move')
        for quant_row in self.browse(cr, SUPERUSER_ID, ids, context=context):
            if quant_row.so_id:
                so_ids.append(quant_row.so_id.id)
            else:
                for move in quant_row.history_ids:
                    if move.picking_id and move.picking_id.picking_type_code == 'incoming'\
                            and move.purchase_line_id:
                        so_id, so_line_id = move_obj.purchase_server_one_sale(cr, SUPERUSER_ID, move.purchase_line_id,
                                                                              quant_row.product_id.id, context=context)
                        if so_id:
                            so_ids.extend(so_id)
        return so_ids

    # def search_quant_and_lock(self, cr, uid, product_id, location_ids, order_line,
    #                           so_id, prod_spec, context=None):
    #功能 的改进然后注释部分代码.
    #     if prod_spec:
    #         prod_spec = '=%s'%prod_spec
    #     else:
    #         prod_spec = ' is NULL'
    #     cr.execute("""SELECT id AS id,
    #                   qty as qty
    #                   from stock_quant where product_id = %s and location_id in %s and
    #                   prod_spec%s and reservation_id is NULL and so_id=%s and so_line_id=%s
    #               """ % (product_id, location_ids, prod_spec, so_id, order_line.id))
    #     quanty_ids = cr.dictfetchall()
    #     if sum([qty for quant_id, qty in quanty_ids.iteritems()]) == order_line.prodcut_uom_qty:
    #         return [quant_id for quant_id, qty in quanty_ids.iteritems()]
    #     raise osv.except_osv(_('Warning!'),
    #                          u'锁定库存失败,为您采购的产品%s,已其他单据锁定或者已出库!' % (order_line.product_id.name))

    def warehouse_lock_quant_by_sale(self, cr, uid, ids, context=None):
        move_obj = self.pool.get('stock.move')
        purchase_obj = self.pool.get('purchase.order')
        purchase_line_obj = self.pool.get('purchase.order.line')
        lock_result = True
        for order_row in self.browse(cr, uid, ids, context=context):
            purchase_ids = purchase_obj.search(cr, SUPERUSER_ID, [('so_code_list', 'ilike', order_row.name),
                                                         ('so_ids', 'ilike', str(order_row.id))], context=context)
            purchase_ids or self.raise_error(u'找不到对应数量的采购单')
            for line in order_row.order_line:
                if line.product_uom_qty <= 0:
                    continue
                if line.shipped_qty != 0:
                    self.raise_error(u'已经建立过出库单，请通过建立出库单的方式锁定库存!')
                purchase_line_ids = purchase_line_obj.search(cr, SUPERUSER_ID, [('order_id', 'in', purchase_ids),
                                                                       ('product_id', '=', line.product_id.id),
                                                                       ], context=context)
                if len(purchase_line_ids) == 1:
                    purchase_line_row = purchase_line_obj.browse(cr, SUPERUSER_ID, purchase_line_ids[0], context=context)
                    lock_result = move_obj.auto_lock_quant_by_sale(cr, SUPERUSER_ID, purchase_line_row, [order_row.id],
                                                     line.id, context=context)
                # if not lock_result:
                #     self.raise_error(u'没有可供锁定的数量!')
        return True

    def raise_error(self, msg):
        raise osv.except_osv(_('Warning!'), msg)

    def _get_invoice_paid(self, cr, uid, ids, context=None):
        return []

    def _get_change_stock_picking(self, cr, uid, ids, context=None):
        so_ids = []
        for picking in self.browse(cr, uid, ids, context=context):
            so_ids.append(picking.so_id.id)
        return so_ids

    def _judge_order_tpe(self, cr, uid, ids, name, args, context=None):
        res = {}
        for order_row in self.browse(cr, uid, ids, context=context):
            res[order_row.id] = all([line_row.product_id.type == 'service' for line_row in order_row.order_line])
        return res

    def _compute_cost_logistics(self, cr, uid, ids, name, args, context=None):
        res = {}
        delivery_obj = self.pool.get('apply.delivery')
        cost_obj = self.pool.get('express.cost')
        picking_obj = self.pool.get('stock.picking')
        for order in self.browse(cr, uid, ids, context=context):
            shipping_codes, cost = [], 0
            if order.is_card:
                for code in delivery_obj.search_read(cr, uid, [('card_so_id', '=', order.id)],
                                                     ['shipping_code'], context=context):
                    shipping_codes.append(code.get('shipping_code'))
            for delivery_row in order.express_delivery_ids:
                if delivery_row.express_code:
                    shipping_codes.append(delivery_row.express_code)
            for code in picking_obj.search_read(cr, uid, [('so_id', '=', order.id), ('tracking_number', '!=', False)],
                                                ['tracking_number'], context=context):
                if ' ' not in code.get('tracking_number'):
                    shipping_codes.append(code.get('tracking_number'))
                else:
                    shipping_codes.extend(code.get('tracking_number', ' ').split(' '))

            cost += sum([float(delivery_row.get('express_cost', 0)) or 0 for delivery_row in
                         cost_obj.search_read(cr, uid, [('express_code', 'in', shipping_codes)],
                                              ['express_cost'], context=context)])
            res[order.id] = cost
        return res

    def _record_access_log(self, cr, uid, ids, name , args , context=None):
        res = {}
        not_have_sale_group = not self.pool['res.users'].has_group(cr, uid, 'base.group_sale_salesman')
        for sale_order in self.browse(cr, uid, ids, context=context):
            if not_have_sale_group:
                self.pool.get("access.log").create(cr, uid, {'user_id': uid, 'access_model': self._name,
                                                             'context': "{'action_id':%s, 'seesion_id':%s}" %
                                                                        (context.get('params', {}).get('action'),
                                                                                                request.session_id),
                                                             'record_id': sale_order.id},
                                                             context=context)
            res[sale_order.id] = sale_order.id
        return res

    def _get_sale_order_number_somedate(self, cr, uid, ids, name, args, context=None):
        res = dict(
            (res_id, {'order_number': 0, 'order_delivery_hour_number': 0}) for res_id in ids)
        for order in self.browse(cr, uid, ids, context=context):
            res[order.id] = {
                'order_number': self.search_count(cr, SUPERUSER_ID, [('state', 'not in', ('draft', 'done')),
                                                            ('order_type_id.order_type_code','=','normal_order'), 
                                                            ('real_so_id', '=', False),
                                                            ('shipping_type_sale', '=', 'self_logistics'),
                                                            ('expected_delivery_time', '=', order.expected_delivery_time)],
                                                  context=context),
                'order_delivery_hour_number': self.search_count(cr, SUPERUSER_ID, [('state', 'not in', ('draft', 'done')),
                                                            ('order_type_id.order_type_code','=','normal_order'), 
                                                            ('real_so_id', '=', False),
                                                            ('expected_delivery_hour', '=', order.expected_delivery_hour),
                                                            ('shipping_type_sale', '=', 'self_logistics'),
                                                            ('expected_delivery_time', '=', order.expected_delivery_time)],
                                                  context=context)}
        return res

    _columns = {
        # 'open_invoice': fields.boolean(u'已开全部发票'),
        'shipment_completion': fields.function(_compute_shipment_completion, string='已发货完成', type='boolean', select=True,
                                               store={
                                                   'stock.picking': (_get_moving_picking, ['state'], 10),
                                                   'sale.order.line': (_determine_shipment_completion, ['shipped_qty',
                                                            'product_uom_qty', 'delivered_qty','state'], 20),
                                               }, copy=False),
        'order_line_change_id': fields.one2many('sale.order.line.change', 'so_id', string='订单行变更历史',copy=False),
        'expected_delivery_time': fields.date(u"送货日期", track_visibility='onchange',copy=False),
        'expected_delivery_hour': fields.selection(
            [(u'白天09:00~17:00', u'白天09:00~17:00'), (u'上午09:00~11:30', u'上午09:00~11:30'),
             (u'中午11:30~13:00', u'中午11:30~13:00'), (u'下午13:00~17:00', u'下午13:00~17:00'),
             (u'晚上19:00~22:00', u'晚上19:00~22:00')], u'期望具体时间',required=True,track_visibility='onchange'),
        'purchase_managed': fields.function(_compute_purchase_order_managerment, string=u'是否采购处理过',
                                            type='boolean', copy=False, select=True,
                    store={'sale.order.line.change': (_get_purchase_order_managerment, ['po_id', 'so_id'], 11),
                           'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line_change_id', 'state'], 11)}),
        'is_exchange': fields.boolean(u'是否是换货单',copy=False, select=True),
        'is_return': fields.boolean(u'是否是退货单',copy=False, select=True),
        'common_address': fields.many2one('customer.address', string=u'常用收货地址',copy=False),
        # 去除掉customer_address，替换成company_contacts
        'contacts_id': fields.many2one('company.contacts', string=u'联系人',copy=False),
        'consignee': fields.char(u'收件人'),
        'street': fields.char(u'详细地址'),
        'zip': fields.char(u'邮政编码'),
        'phone_number': fields.char(u'电话号', size=64),
        'mobile_number': fields.char(u'手机号', size=64),
        'sale_order_invoice': fields.many2one('official.invoice', string='纸质发票信息', copy=False),
        'reconciled_num': fields.function(_get_reconciled_info, type='float', string='已核销金额',
                                          store={
                                              'sale.order.line': (_get_sale_order_line_invoice_num, ['invoiced'], 15),
                                              'account.invoice': (_get_all_invoiced_order, ['residual'], 15),
                                          }, copy=False),
        'invoiced_num': fields.function(_get_invoiced_info, type='float', string='已开票',
                                        store={
                                            'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['invoice_ids'], 11),
                                            'account.invoice': (_get_all_invoiced_order, ['state'], 11),
                                        }, copy=False),
        'payment_method': fields.selection([(u'转账', u'转账'), (u'现金', u'现金'), (u'支票', u'支票'), (u'POS机', u'POS机')],
                                       string='付款方式', required=False),
        'print_state': fields.boolean(u'打印状态', track_visibility='onchange', copy=False),
        'profit_deduct_type': fields.many2one('profit.deduct.type', string='毛利提成类型',copy=False),
        'real_so_id': fields.many2one('sale.order', string='真单编号', copy=False, select=True),
        'invoiced': fields.function(_invoiced, string='Paid',
                                    fnct_search=_invoiced_search, type='boolean',
                                    help="It indicates that an invoice has been paid.",copy=False),
        'gross_profit_total': fields.function(_compute_gross_profit_total, type="float", string="毛利合计",
                                              store={'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['order_line'], 20)}
                                              , copy=False),
        'shipping_type_sale': fields.selection([(u'express_delivery', u'快递'), (u'sale_delivery', u'销售送货'),
                                                (u'self_logistics', u'公司物流'), ('direct_delivery', u'供应商直送'),('take_car', u'打车')],
                                               string=u"送货方式",copy=False),
        'order_line': fields.one2many('sale.order.line', 'order_id', 'Order Lines', copy=True),
        'partner_name': fields.char(u'客户名称'),
        'is_project': fields.boolean(u'是否方案',copy=False),
        'is_gross_profit_checked': fields.function(compute_is_gross_profit_checked,  type='boolean',
                                                   store={
                                                    'sale.order.line': (sale_order_line_is_gross_profit_checked,
                                                                        ['is_gross_profit_checked'], 11),
                                                    }, string='未全部核对', copy=False),
        'is_reconciled': fields.function(_compute_is_reconciled, string="是否核清", type="boolean", select=True,
                                         store={'sale.order':(lambda self, cr, uid, ids, c={}: ids,['invoice_ids'], 15),
                                                'account.invoice':(_get_all_invoiced_order, ['state', 'residual'], 15),
                                                'sale.order.line': (_get_sale_order_line_invoice_num, ['invoiced'], 15),
                                                },copy=False),
        'delivery_users': fields.function(_compute_delivery_user, string="送货人", type="char",
                                          store={'stock.picking': (_get_change_stock_picking,
                                                                   ['delivery_user_id'], 10)}, copy=False),
        'date_order': fields.datetime(string=u'首次确认时间', readonly=True, required=False,copy=False),
        'is_check_stock_enough': fields.boolean(u'检查已满足库存', copy=False),
        'last_check_stock_date': fields.datetime(u'检查库存时间', copy=False),
        'inner_note': fields.char(u'内部备注',copy=False),
        'stock_note': fields.char(u'仓库备注',copy=False),
        'finance_note': fields.char(u'财务备注',copy=False),
        'is_tallying_printed': fields.boolean(u'理货单是否已打印',copy=False),
        'is_invisible_in_map': fields.boolean(u'在送货地图中隐藏',copy=False),
        'share_to_uid': fields.many2one('res.users', string='共享给',copy=False),
        'cust_grade': fields.char(u'客户级别', copy=False),
        #'share_to_uid': fields.related('partner_id', 'share_to_uid', type='many2one', relation='res.users', string=u'共享给', readonly="1", copy=False),
        'create_date': fields.datetime(u'订单创建时间', copy=False),
        #'image_tally': fields.binary(u"理货单图片", help=u"通过微信发送的理货单照片"),
        'is_sample': fields.boolean(u'是否样品订单', copy=False),
        'cost_logistics': fields.function(_compute_cost_logistics, string=u'物流成本', type='float', readonly="1"),
        'is_need_purchase_audit': fields.function(judge_is_need_purchase_audit, string='是否需要采购审核', type='boolean', select=True,
                                                 store={'sale.order': (lambda self, cr, uid, ids, c={}: ids,
                                                                       ['is_exchange', 'is_return',
                                                                        'expected_delivery_time', 'shipping_type_sale',
                                                                        'is_card','date_order', 'expected_delivery_time',
                                                                        'state', 'expected_time_confirmed'], 10)}, copy=False),
        'need_package': fields.selection([(u'否', u'否'), (u'是', u'是')], string='是否需要包装', required=False, copy=False),
        'is_locked_quant': fields.function(_compute_quant_is_reservate, type='boolean', copy=False, store={
            'stock.quant': (_get_locked_quant, ['so_id'], 10)
        }),
        'tally_printer_uid': fields.many2one('res.users', string='理货单打印人', copy=False),
        'tally_print_time': fields.datetime(u'理货单打印时间', copy=False ),
        'expected_time_confirmed': fields.boolean(u'期望发货日期已确认', copy=False),
        'origin_so_id': fields.many2one('sale.order', string='原订单', select=True),
        'return_so_ids': fields.one2many('sale.order', 'origin_so_id', string='退单列表', select=True),
        'is_has_return_change': fields.boolean(u'有退换货单', copy=False),
        'is_write_off': fields.boolean(u'是否核销过'),
        'is_service_order': fields.function(_judge_order_tpe, type='boolean',
                                            store={'sale.order': (lambda self, cr, uid, ids, c={}: ids,
                                                                  ['order_line'], 10)}, string=u'是否是服务类型订单'),
        'record_access_log': fields.function(_record_access_log, u'记录访问人', type='char'),
        'is_shop_order': fields.boolean(u'是否商城订单'),
        'is_gift_order': fields.boolean(u'是否送礼订单'),
        'order_number': fields.function(_get_sale_order_number_somedate, string='送货订单数', multi='order_number', type='integer',
                store={'sale.order': (lambda self, cr, uid, ids, c={}: ids,['expected_delivery_time', 'expected_delivery_hour', 'shipping_type_sale'],10 )}),
        'order_delivery_hour_number': fields.function(_get_sale_order_number_somedate, string='送货订单数', multi='order_number', type='integer',
                store={'sale.order': (lambda self, cr, uid, ids, c={}: ids, ['expected_delivery_time', 'expected_delivery_hour', 'shipping_type_sale'],10)}
                                                        ),
    }
    
    def sale_reverse_write_off(self, cr, uid, ids, context=None):
        for id in ids:
            self.write(cr, uid, id, {'is_write_off': True}, context=context)
        return True
    
    def confirm_expected_delivery_time(self, cr, uid, ids, context=None):
        for order_row in self.browse(cr, uid, ids, context=context):
            if order_row.state in ('cancel', 'draft'):
                self.raise_error(u'当前页面已修改请刷新后重试!')
            order_row.write({'expected_time_confirmed': True})
        return True
    
    def set_default_expected_delivery_time(self, cr, uid, context=None):
        week_day = {3: {2: 2, 3: 2, 4: 2, 5: 1}, 4: {1: 2, 2: 2, 3: 2, 4: 2, 5: 1}}
        if datetime.now(pytz.timezone("Asia/Shanghai")).strftime(ISODATETIMEFORMAT) >\
                datetime.now(pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d 15:00:00"):
            few_days = 4
        else:
            few_days = 3
        few_days += (week_day.get(few_days)).get(datetime.now().weekday(), 0)
        return (datetime.now() + timedelta(days=few_days)).strftime(ISODATETIMEFORMAT)
    _defaults = {
        # 设置期望发货日期的默认值
        'shipping_type_sale': False,
        'date_order': False,
        'expected_time_confirmed': True,
        'expected_delivery_hour': u'白天09:00~17:00',
        'expected_delivery_time': set_default_expected_delivery_time,
        'purchase_managed': True
        # 'profit_deduct_type': lambda s, cr, uid, ctx={}: s.pool.get('profit.deduct.type').search(cr, uid, [('gross_profit_margin', '=', '0')], offset=0, limit=1, context=ctx)[0],
    }
# class  mail_thread(osv.AbstractModel):
#     _inherit = 'mail.thread'
#     def _get_tracked_fields(self, cr, uid, updated_fields, context=None):
#         """ Return a structure of tracked fields for the current model.
#             :param list updated_fields: modified field names
#             :return dict: a dict mapping field name to description, containing
#                 always tracked fields and modified on_change fields
#         """
#         tracked_fields = []
#         for name, field in self._fields.items():
#             visibility = getattr(field, 'track_visibility', False)
#             if visibility == 'always' or (visibility == 'onchange' and name in updated_fields) or name in self._track or  (visibility == 'onchange' and name==amount_untaxed and updated_fields==['order_line']):
#                 tracked_fields.append(name)

#         if tracked_fields:
#             return self.fields_get(cr, uid, tracked_fields, context=context)
#         return {}


class sale_order_line(osv.osv):
    _inherit = 'sale.order.line'
    _order = 'id'

    def create(self, cr, uid, vals, context=None):
        """1.新建采购单行的时候 自动填充 产品的原件 和销售价 以放便保留原始数据
        2.当订单已核对的时候 自动填充订单核对时间"""
        if vals.get('prod_spec') :
            vals.update({'prod_spec': vals.get('prod_spec') and (vals.get('prod_spec')).strip()})
        if vals.get('product_id'):
            product_obj = self.pool.get('product.product').browse(
                cr, uid, vals.get('product_id'), context=context)
            vals.update({'cost_price': product_obj.standard_price, 'origin_price': product_obj.list_price,
                         'name': '[%s] %s' % (product_obj.default_code, product_obj.name)})
        # if vals.get('is_gross_profit_checked'):
        #     vals.update({'check_uid': uid, 'gross_profit_checked_time': datetime.now()})
        route_id = context.get('route_id')
        if route_id:
            vals.update({'route_id': route_id})
        return_val = super(sale_order_line, self).create(cr, uid, vals, context=context)
        # if vals.get('is_gross_profit_checked'):
        #     self.gross_profit_rate_type(cr, uid, return_val, context=context)
        # if vals.get('cost_price'):
        #     self.gross_profit_rate_type(cr, uid, return_val, context=context)
        return return_val

    def return_gross_profit_checked(self, cr, uid, ids, context=None):
        for sale_order_line_objs in self.browse(cr, uid, ids, context=context):
            sale_order_line_objs.write({'is_gross_profit_checked': False, 'gross_profit_checked_time': False})
        return True
    
    def unlock_order_line(self, cr, uid, line, context=None):
        quant_obj = self.pool.get('stock.quant')
        quant_ids = quant_obj.search(cr, SUPERUSER_ID, [('so_id', '=', line.order_id.id),
                                                        ('product_id', '=', line.product_id.id)], context=context)
        if quant_ids:
            quant_obj.write(cr, SUPERUSER_ID, quant_ids, {'reservation_id': False, 'so_id': False, 'so_line_id': False},
                        context=context)
        return True

    def unlink(self, cr, uid, ids, context=None):
        for line in self.browse(cr, uid, ids, context=context):
            self.unlock_order_line(cr, uid, line, context=context)
            if line.order_id.invoice_exists:
                raise osv.except_osv(_('Warning!'), u'销售订单%s已有发票不允许删除，请先删除系统发票(取消的也要删除)！' % line.order_id.name )
            if line.product_id_type == 'service':
                line.write({'state': 'draft'})
        return_vals = super(sale_order_line, self).unlink(cr, uid, ids, context=context)
        return return_vals

    def write(self, cr, uid, ids, vals, context=None):
        """1. 如果已开发票则不允许 修改单价
        2.当订单已核对的时候 自动填充订单核对时间"""
        if vals.get('prod_spec'):
            vals.update({'prod_spec': vals.get('prod_spec') and (vals.get('prod_spec')).strip()})
        if vals.get('is_gross_profit_checked'):
            vals.update({'check_uid': uid, 'gross_profit_checked_time': datetime.now()})
        if isinstance(ids, int):
            ids = [ids]
        for so_line in self.browse(cr, uid, list(set(ids)), context=context):
            if vals.get("cost_price") and float_compare(vals.get('cost_price'), so_line.cost_price,
                                                        precision_digits=2) != 0 and so_line.is_gross_profit_checked:
                vals.update({"is_gross_profit_checked": False, 'gross_profit_checked_time': False})
            # if so_line.shipped_qty != so_line.product_uom_qty and vals.get("is_gross_profit_checked", False) \
            #     and so_line.product_id.type != 'service':
            #     raise osv.except_osv(u'错误', u"未出库不建议进行毛利核对，如果出库成本价不同会导致无法出库！") #暂时去掉。
            if vals.get('product_uom_qty') and so_line.locked_quant_qty != 0:
                self.unlock_order_line(cr, uid, so_line, context=context)
            #判断已建立发货单数量是否超出
            if vals.get('delivered_qty'):
                if vals.get('delivered_qty') < 0:
                    raise osv.except_osv(_('Warning!'), u'"%s"产品已建立发货单数不能小于0' % (so_line.name))
                product_uom_qty = so_line.product_uom_qty
                #如果是销售产品数量大于0
                if product_uom_qty > 0 and vals.get('delivered_qty') > product_uom_qty:
                    raise osv.except_osv(_('Warning!'), u'"%s"产品已建立发货单数%s超过了销售订单产品数量%s'
                                     % (so_line.product_id.name, vals.get('delivered_qty'), product_uom_qty))
                #如果是销售产品数量小于0
                if product_uom_qty < 0 and vals.get('delivered_qty') > abs(product_uom_qty):
                    raise osv.except_osv(_('Warning!'), u'"%s"产品已建立退货入库单数%s超过了退货订单产品数量%s'
                                     % (so_line.product_id.name, vals.get('delivered_qty'), abs(product_uom_qty)))

            #判断是否有未取消的系统发票
            if vals.get('price_unit', 'default_values') != 'default_values' and \
                    float_compare(so_line.price_unit, vals.get('price_unit', 'default_values'), precision_digits=2) != 0:
                for invoce in so_line.order_id.invoice_ids:
                    if invoce.state != 'cancel':
                        raise osv.except_osv(_('Warning!'),
                                             u'本销售订单存在未取消的发票，不能修改产品价格，请取消发票后再进行修改价格！')
            #检查是否已经毛利核对
            if vals.get('is_gross_profit_checked'):
                #暂时取消成本价不能为0的限制
                if so_line.cost_price < 0:
                    raise osv.except_osv(_('Warning!'), u'产品成本价不能为零,请修改后再进行保存!')
            # if vals.get('is_gross_profit_checked') or vals.get('price_unit') or vals.get('cost_price')\
            #         or vals.get('cost_tax') or vals.get('has_input_invoice'):
            #     self.gross_profit_rate_type(cr, uid, so_line.id, context=context)
            if vals.get('product_id'):
                self.unlock_order_line(cr, uid, so_line, context=context)
                product_obj = self.pool.get('product.product').browse(
                    cr, uid, vals.get('product_id'), context=context)
                vals.update({'name': '[%s] %s' % (product_obj.default_code, product_obj.name)})
                self.handle_change_product_id(cr, uid, so_line, context=context)
        res = super(sale_order_line, self).write(cr, uid, list(set(ids)), vals, context=context)
        if 'is_gross_profit_checked' in vals or 'cost_price' in vals or 'cost_tax' in vals or \
            'has_input_invoice' in vals or 'price_unit' in vals:
            self.gross_profit_rate_type(cr, uid, ids, context=context)
        return res
    
    def handle_change_product_id(self, cr, uid, so_line, context=None):
        change_obj = self.pool.get('sale.order.line.change')
        change_ids = change_obj.search(cr, uid, [('so_line_id', '=', so_line.id)], context=context)
        if change_ids:
            change_obj.write(cr, uid, change_ids, {'so_line_id': False}, context=context)
        
    def _get_default_undelivered_qty(self, cr, uid, ids, undelivered_qty, arg, context=None):
        # 计算出未建立发货单的数量
        res = {}
        sale_order_lines_obj = self.browse(cr, uid, ids, context=context)
        if sale_order_lines_obj:
            for sale_order_line in sale_order_lines_obj:
                res[sale_order_line.id] = abs(sale_order_line.product_uom_qty) - \
                    abs(sale_order_line.delivered_qty)
        return res

    def onchange_prod_spec(self, cr, uid, ids, product_id, prod_spec, sale_order_name, context=None):
        # 采购处理过的型号 不允许修改
        sale_order_id = self.pool.get('sale.order').search(
            cr, uid, [('name', '=', sale_order_name)], context=context)
        if sale_order_id:
            order_line_change_id = self.pool.get('sale.order.line.change').search(
                cr, uid, [('product_id', '=', product_id), ('so_id', '=', sale_order_id[0]), ('po_id', '!=', False)], context=context)
            if order_line_change_id:
                for order_line_change in self.pool.get('sale.order.line.change').browse(
                        cr, uid, order_line_change_id, context=context):
                    if order_line_change.prod_spec != prod_spec:
                        return {'value': {'prod_spec': order_line_change.prod_spec},
                                'warning': {'title': u'错误', 'message': u'采购已处理在产品' + order_line_change.product_id.name + "的型号" + str(order_line_change.prod_spec) + "不允许修改成其他型号"}}
            return {'value': {'prod_spec': prod_spec}}

    def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
                          uom=False, qty_uos=0, uos=False, name='', partner_id=False,
                          lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
        # 继承系统自带的 方法，改变产品的时候进行一些参数的添加
        all_return = super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty,
                                                                    uom, qty_uos, uos, name, partner_id,
                                                                    lang, update_tax, date_order, packaging, fiscal_position, flag, context)
        if all_return.get('value', False) and all_return.get('value').get('price_unit', False):
            all_return["value"].update({'origin_price': all_return["value"].get('price_unit')})
            mininum_discount = self.pool.get('ir.config_parameter').search(
                cr, uid, [('key', '=', 'sale.mininum_discount')], context=context)[0]
            mininum_discount_obj = self.pool.get('ir.config_parameter').browse(
                cr, uid, mininum_discount, context=context)
            if all_return['value']['price_unit'] > 0:
                all_return['value'].update({'minimum_price': all_return['value'][
                                           'price_unit'] * float(mininum_discount_obj.value), 'price_subtotal': all_return['value'][
                                           'price_unit']* qty})
        return all_return

    def dftg_product_id_change_with_wh(self, cr, uid, ids, pricelist, product, prod_spec, sale_order_name, qty=0,
                                       uom=False, qty_uos=0, uos=False, name='', partner_id=False,
                                       lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, warehouse_id=False, parent_states=False, context=None):
        """ 继承系统原有的onchange 并对返回值进行加工(sale_stock 模块继承修改了 product_id_onchange 为了保留这个模块的这部分功能所以继承这个函数)
        1.如果此订单存在有已处理的订单变更历史,则再次出现这个产品时 型号和已处理的订单变更历史中的型号一致"""
        return_vals = self.pool.get("sale.order.line").product_id_change_with_wh(cr, uid, ids, pricelist, product, qty,
                                                                                 uom, qty_uos, uos, name, partner_id,
                                                                                 lang, update_tax, date_order, packaging, fiscal_position, flag, warehouse_id=warehouse_id, context=context)
        if not (parent_states == 'draft'):
            # BUG FIX 在确认状态下依旧可以新建只是raise 报错并不能阻止添加的动作!
            return {'value': {'product_id': False},
                    'warning': {'title': u'错误',
                                'message': u'销售订单在确认状态下不能新建订单行，请申请修改后再添加'}}

        if return_vals.get('warning'):
            return_vals.update({'warning': ''})
        sale_order_id = self.pool.get('sale.order').search(
            cr, uid, [('name', '=', sale_order_name)], context=context)
        # model_data_obj = self.pool.get('ir.model.data')
        # card_exchange_order_id = model_data_obj.xmlid_to_res_id(
        #     cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
        if sale_order_id:
            #sale_order_obj = self.pool.get('sale.order').browse(cr,uid,sale_order_id,context=context)
            # if sale_order_obj.order_type_id.id == card_exchange_order_id:   # 需求变更，价格的修改依旧要有权限的人才能修改
            #     return_vals['value'].update(
            #         {'price_unit': 0})
            order_line_change_id = self.pool.get('sale.order.line.change').search(
                cr, uid, [('product_id', '=', product), ('so_id', '=', sale_order_id[0]), ('po_id', '!=', False)], context=context)
            if order_line_change_id:
                for order_line_change in self.pool.get('sale.order.line.change').browse(
                        cr, uid, order_line_change_id, context=context):
                    if order_line_change.prod_spec != prod_spec:
                        return_vals['value'].update(
                            {'prod_spec': order_line_change.prod_spec})
                        return return_vals
        return return_vals
        # def create(self, cr, uid, vals, context=None):
        # mininum_discount = self.pool.get('ir.config_parameter').search(
        # cr, uid, [('key', '=', 'sale.mininum_discount')], context=context)[0]
        # mininum_discount_obj = self.pool.get('ir.config_parameter').browse(
        # cr, uid, mininum_discount, context=context)
        # if vals.get('price_unit') > 0:
        # vals.update({'minimum_price': vals.get('price_unit')
        # * float(mininum_discount_obj.value)})
        # return super(sale_order_line, self).create(cr, uid, vals,
        # context=context)

    def onchange_price_unit(self, cr, uid, ids, product_id, price_unit, minimum_price, context=None):
        """销售订单的销售 价格 不允许小于订单的最低售价 (无论是新建还是修改！)"""
        sale_order_obj = self.pool.get("sale.order")
        order_line_id_list = sale_order_obj.search(cr, uid, [("order_line", "in", ids)], context=context)
        order_line_obj_list = sale_order_obj.browse(cr, uid, order_line_id_list, context=context)
        if minimum_price > 0 and not order_line_obj_list or (order_line_obj_list and not order_line_obj_list[0].real_so_id):
            # model_data_obj = self.pool.get('ir.model.data')
            # card_exchange_order_id = model_data_obj.xmlid_to_res_id(
            #     cr, SUPERUSER_ID, 'dftg_ext.dftg_order_type_data_card_exchange_order')
            users = self.pool.get('res.users').browse(
                cr, uid, uid, context=context)
            expenses_ids = self.pool.get('sale.order.product.expenses').search(cr, uid,
                           [('product_id', '=', product_id)], context=context)
            if users[0].has_group('dftg_ext.unlimited_discount') or expenses_ids:
                return {'value': {'price_unit': price_unit}}
            # 提货订单的订单行价格强制为0，不进行价格改0权限的判断
            # if sale_order_obj and sale_order_obj.order_type_id.id==card_exchange_order_id: # 需求变更，价格的修改依旧要有权限的人才能修改
            #     return {'value': {'price_unit': 0}}
            if price_unit < minimum_price:
                return {'value': {'price_unit': minimum_price}, 'warning': {'title': u'错误', 'message': u"销售价格不能小于%s" % (minimum_price)}}
        return {}

    def onchange_delivery_quantity_qty(self, cr, uid, ids, purchase_processed, undelivered_qty, delivery_quantity_qty, context=None):
        # 用于 判断所填写的发货数量的正确性
        if not purchase_processed:
            return_vals = {'warning': {'title': u'错误', 'message': u"采购未处理，不能创建发货单！"}, 'value': {'delivery_quantity_qty':0}}
        elif delivery_quantity_qty < 0:
            return_vals = {'warning': {'title': u'错误', 'message': u"本次发货数量不能小于零"}, 'value': {'delivery_quantity_qty': abs(undelivered_qty)} }
        elif abs(delivery_quantity_qty) > abs(undelivered_qty):
            return_vals = {'warning': {'title': u'错误', 'message': u"本次发货数量不能大于未发货数"}, 'value': {'delivery_quantity_qty': abs(undelivered_qty)}}
        else:
            return_vals = False
        return return_vals

    def product_id_change_dftg(self, cr, uid, ids, pricelist, product, qty=0,
                               uom=False, qty_uos=0, uos=False, name='', partner_id=False,
                               lang=False, update_tax=True, date_order=False, packaging=False, fiscal_position=False, flag=False, context=None):
        """ 限制销售订单的的数量不能为零"""
        sales_order_line_obj = self.browse(cr, uid, ids, context=context)
        res = self.product_id_change(cr, uid, ids, pricelist, product, qty, uom, qty_uos, uos, name,
                                     partner_id, lang, update_tax, date_order, packaging, fiscal_position, flag, context=context)
        if 'price_unit' in res["value"]:
            del res["value"]['price_unit']
        if qty == 0:
            return {'warning': {'title': u'错误', 'message': u"订单产品数量不能为零"},
                    'value': res["value"],
                    }
        if abs(qty) < sales_order_line_obj.delivered_qty or abs(qty) < sales_order_line_obj.shipped_qty:
            res["value"].update({"product_uom_qty": sales_order_line_obj.delivered_qty})
            res.update({'warning': {'title': u'错误', 'message': u"销售订单产品数量不能小于已建发货单数！"}})
        return res

    def _compute_gross_profit(self, cr, uid, ids, name, arg, context=None):
        # [add ]  # 579 销售订单行的gross_profit字段，需要在修改产品数量或者修改产品价格时就根据产品的成本价来进行“毛利”的计算
        res = {}
        for sale_order_line_obj in self.browse(cr, uid, ids, context=context):
            res[sale_order_line_obj.id] = sale_order_line_obj.product_uom_qty * \
                (sale_order_line_obj.price_unit - sale_order_line_obj.cost_price)
        return res

    def _get_all_invoiced_order(self, cr, uid, ids, context=None):
        order_id = {}
        for picking in self.pool.get('stock.picking').browse(cr, uid, ids, context=context):
            if picking.origin and picking.origin[0] == 'S':
                sale_order_id = self.pool.get("sale.order").search(
                    cr, uid, [('name', '=', picking.origin)], context=context)
                for order_line_id in self.pool.get("sale.order.line").search(cr, uid, [('order_id', '=', sale_order_id[0])], context=context):
                    order_id[order_line_id] = True
        return order_id.keys()

    # 计算毛利率(公式：毛利/成本价格)
    def gross_profit_rate_type(self, cr, uid, ids, context=None):
        deduct_type_obj = self.pool.get("profit.deduct.type")
        sales_staff_commission = 0
        sales_assistant_commission = 0
        for sale_order_line in self.browse(cr, uid, ids, context=context):
            date_order = sale_order_line.order_id.date_order and sale_order_line.order_id.date_order[0: 10]
            if sale_order_line.shipped_qty != sale_order_line.product_uom_qty and\
                    not sale_order_line.is_gross_profit_checked:
                return True
            price_unit = sale_order_line.price_unit
            cost_price = sale_order_line.cost_price + sale_order_line.cost_tax * sale_order_line.cost_price
            gross_profit_rate = 0
            sales_staff_commission = 0
            sales_assistant_commission = 0
            if cost_price != 0:
                subtotal = price_unit * sale_order_line.product_uom_qty
                gross_profit_rate = (price_unit - cost_price) / cost_price
                # gross_profit_rate_char = gross_profit_rate * 100
                gross_profit_rate_char = float_round(gross_profit_rate * 100, precision_digits=2)
                deduct_type_id = deduct_type_obj.search(
                    cr, uid, [('gross_profit_margin_min', '<=', gross_profit_rate),
                              ('gross_profit_margin_top', '>', gross_profit_rate),
                              '|', ('end_date', '>=', date_order),
                              ('end_date', '=', False),
                              ], limit=1, order='end_date, gross_profit_margin asc',
                     context=context)
                deduct_type_row = deduct_type_obj.browse(cr, uid, deduct_type_id and deduct_type_id[0],
                                                         context=context)
                sales_staff_commission = float_round(subtotal * deduct_type_row.sales_staff_commission, precision_digits=2)
                sales_assistant_commission = float_round(subtotal * deduct_type_row.sales_assistant_commission,
                                                         precision_digits=2)
            else:
                deduct_type_id = self.pool.get("profit.deduct.type").search(
                    cr, uid,  [('gross_profit_margin_min', '<=', gross_profit_rate),
                               ('gross_profit_margin_top', '>', gross_profit_rate),
                               '|', ('end_date', '>=', date_order),
                               ('end_date', '=', False),
                               ], limit=1, order='end_date, gross_profit_margin asc',
                     context=context)
                gross_profit_rate_char = gross_profit_rate * 100 # "%s%%" % (str(gross_profit_rate * 100))
            sale_order_line.write({'profit_deduct_type_id': deduct_type_id and deduct_type_id[0],
                                   'gross_profit_rate': gross_profit_rate_char,
                                   'sales_staff_commission': sales_staff_commission,
                                   'sales_assistant_commission': sales_assistant_commission})
        return True

    # 计算折扣
    def _compute_discount(self, cr, uid, ids, name, arg, context=None):
        res = {}
        for sale_order_line in self.browse(cr, uid, ids, context=context):
            if sale_order_line.origin_price != 0:
                res[sale_order_line.id] = float_round((sale_order_line.price_unit - sale_order_line.origin_price) / sale_order_line.origin_price, precision_digits=2)
            else:
                res[sale_order_line.id] = 0
        return res

    # # 毛利已核对打勾操作 由于业务需求 毛利核对时间改在 write的时候进行写入
    # def gross_profit_check(self, cr, uid, ids, is_gross_profit_checked, context=None):
    #     res = {'value': {}}
    #     if is_gross_profit_checked:
    #         res["value"].update({"gross_profit_checked_time": datetime.now()})
    #     else:
    #         res["value"].update({"gross_profit_checked_time": None})
    #     return res

    def _compute_cost_price_subtotal(self, cr, uid, ids, name, arg, context=None):
        # 计算折扣
        res = {}
        for sale_order_line in self.browse(cr, uid, ids, context=context):
            res[sale_order_line.id] = sale_order_line.cost_price * sale_order_line.product_uom_qty
        return res

    def _compute_not_shipped_qty(self,cr,uid,ids,name,arg,context=None):
        """计算还没出库的产品的数量"""
        res={}
        for order_line in self.browse(cr,uid,ids,context=context):
            res[order_line.id]= order_line.delivered_qty < order_line.product_uom_qty
        return res

    def onchange_has_input_invoice(self, cr, uid, ids, has_input_invoice, context=None):
        """
        # 没有进项税（发票了 要有固定的税  0.06）
        :param has_input_invoice: 是否有进项税发票
        """
        sale_order_line_row = None
        if ids:
            sale_order_line_row = self.pool.get('sale.order.line').browse(cr, uid, ids[0], context=context)
        if has_input_invoice:
            return_vals = {'cost_tax': 0}
        else:
            if sale_order_line_row and datetime.strptime(sale_order_line_row.order_id.create_date, ISODATETIMEFORMAT) <= datetime.strptime('2018-08-15 15:59:59', ISODATETIMEFORMAT):
                return_vals = {'cost_tax': 0.06}
            else:
                return_vals = {'cost_tax': 0.08}
        return {'value': return_vals}

    def fields_get(self, cr, uid, allfields=None, context=None, write_access=True, attributes=None):
        res = super(sale_order_line, self).fields_get(cr, uid, allfields, context, write_access, attributes)
        user_obj = self.pool.get('res.users')
        if context.get('invisible_field'):
            if not user_obj.has_group(cr, uid, 'base.group_sale_manager') and \
                    not user_obj.user_has_groups(cr, uid,
                                                 groups='base.group_sale_manager,purchase.group_purchase_manager',
                                                 context=context):
                [res[key].update({'invisible': True}) for key in ['gross_profit_rate', 'cost_price_subtotal',
                                                                  'cost_price_test',
                                                                 'cost_price', 'gross_profit']]
        return res

    def _compute_locked_quant_qty(self, cr, uid, ids, name, args, context=None):
        quant_obj = self.pool.get('stock.quant')
        res = {}
        for order_line_row in self.browse(cr, uid, ids, context=context):
            quant_ids = quant_obj.search(cr, SUPERUSER_ID, [('so_line_id', '=', order_line_row.id),('reservation_id', '=', -1)], context=context)
            res[order_line_row.id] = sum([q.qty for q in quant_obj.browse(cr, SUPERUSER_ID, quant_ids, context=context)])
        return res

    def _get_locked_quant(self, cr, uid, ids, context=None):
        """ 获取库存锁定有变化的销售订单行IDS 以方便计算 """
        so_line_ids = []
        move_obj = self.pool.get('stock.move')
        for quant_row in self.browse(cr, SUPERUSER_ID, ids, context=context):
            if quant_row.so_id:
                so_line_ids.append(quant_row.so_line_id.id)
            else:
                for move in quant_row.history_ids:
                    if move.picking_id and move.picking_id.picking_type_code == 'incoming'\
                            and move.purchase_line_id:
                        so_id, so_line_id = move_obj.purchase_server_one_sale(cr, SUPERUSER_ID, move.purchase_line_id,
                                                                              quant_row.product_id.id, context=context)
                        if so_id:
                            so_line_ids.extend(so_line_id)
        return so_line_ids

    def get_salesman_id(self, cr, uid, ids, name, args, context=None):
        """计算字段： 获取销售订单的的销售员"""
        res = {}
        for order_line in self.browse(cr, uid, ids, context=context):
            res[order_line.id] = order_line.order_id.user_id.id
        return res

    def get_share_to_uid(self, cr, uid, ids, name, args, context=None):
        """计算字段：把销售订单的 共享信息同步到，销售订单行上面"""
        res = {}
        for order_line in self.browse(cr, uid, ids, context=context):
            res[order_line.id] = order_line.order_id.share_to_uid.id
        return res

    def get_change_salesman_order_line(self, cr, uid, ids, context=None):
        """计算字段：监测销售订单行上的销售员是否变化。"""
        line_ids = []
        for sale_row in self.browse(cr, uid, ids, context=context):
            line_ids.extend([line.id for line in sale_row.order_line])
        return line_ids
    
    def _compute_purchase_processed(self, cr, uid, ids, name, args, context=None):
        """计算字段：计算采购是否处理完成"""
        res = {}
        change_obj = self.pool.get('sale.order.line.change')
        for so_line_id in ids:
            change_ids = change_obj.search(cr, uid, [('so_line_id', '=', so_line_id)], context=context)
            res[so_line_id] = all([change_row.po_id for change_row in change_obj.browse(cr, uid, change_ids, context=context)])
        return res
    
    def _purchase_processed_search(self, cr, user, obj, name, args, context=None):
        """
        计算字段： 为了方便的搜索计算字段，于是就加了fuction search 逻辑是按照function 字段的逻辑 找出这一部分符合条件的IDS
        适用于 比较简单的function 字段，如果字段计算逻辑比较复杂， function search 会很难写
        """
        cr.execute("""SELECT line.id, sum(change.change_quantity) FROM sale_order_line AS line
                      LEFT JOIN sale_order_line_change AS change  ON change.so_line_id=line.id
                      WHERE change.po_id IS NULL
                      GROUP BY line.id""")
        res = dict(cr.fetchall())
        if args[0] in [('purchase_processed', '!=', False), ('purchase_processed', '=', True)]:
            operator = 'not in'
        else:
            operator = 'in'
        return [('id', operator, res.keys())]
    
    def _compute_stock_out_locked_quant(self, cr, uid, ids, name, args, context=None):
        """计算出被销售订单锁定 的库存数， 其中包涵
        1.入库数量是专门为 本订单采购的（一入库就锁定，生成出库单后以另外的方式锁定， 一般是全部锁定，本产皮完全满足本订单ps：不包括后面进行修改的情况）
        2.销售订单生成出库单后assign 之后会被出库单锁定库存，（此时可能是锁定一部分）
        """
        res = {}
        move_obj = self.pool.get('stock.move')
        for line_row in self.browse(cr, uid, ids, context=context):
            move_ids = move_obj.search(cr, uid, [('so_line_id', '=', line_row.id)], context=context)
            reserve_qty = line_row.locked_quant_qty
            if move_ids:
                for move_row in move_obj.browse(cr, uid, move_ids, context=context):
                    if move_row.state != 'done':
                        reserve_qty += sum([reserve_row.qty for reserve_row in move_row.reserved_quant_ids])
                    else:
                        reserve_qty += move_row.product_uom_qty
            res[line_row.id] = reserve_qty
        return res

    def _get_all_purchase_user(self, cr, uid, ids, name, args, context=None):
        """
        获取本条销售记录的 所有采购员，可能一条销售记录有着好几条采购记录
        """
        res = {}
        change_obj = self.pool.get('sale.order.line.change')
        for id in ids:
            change_ids = change_obj.search(cr, uid, [('so_line_id', '=', id), ('purchase_user_id', '!=', False)], context=context)
            if change_ids:
                res[id] = '、'.join([change_row.purchase_user_id.name for change_row in change_obj.browse(cr, uid, change_ids, context=context)])
            else:
                res[id] = '无'
        return res

    _columns = {
        'not_shipped_qty':fields.function(_compute_not_shipped_qty, type='boolean',string='未发货完成',store={
            'sale.order.line': (
            lambda self, cr, uid, ids, c={}: ids, ['product_uom_qty', "shipped_qty"], 10),
        }, copy=False),
        'refresh_available_qty': fields.float(u'库存数', digits_compute=dp.get_precision('Product Unit of Measure'), copy=False),
        'undelivered_qty': fields.function(_get_default_undelivered_qty, type="float", string='未建立发货单数量',
                                           digits_compute=dp.get_precision('Product Unit of Measure'), copy=False),
        'delivered_qty': fields.float(u'已建发货单数量', digits_compute=dp.get_precision('Product Unit of Measure'), copy=False),
        'shipped_qty': fields.float(u'已发货数量', digits_compute=dp.get_precision('Product Unit of Measure'), copy=False),
        'delivery_quantity_qty': fields.float(u'本次发货数量', digits_compute=dp.get_precision('Product Unit of Measure'), copy=False),
        'cost_price': fields.float(u'产品成本价', copy=False),
        'cost_price_test': fields.float(u'測試产品成本价', copy=False),
        'gross_profit': fields.function(_compute_gross_profit, string='毛利', type='float',
                                        store={
                                            'sale.order.line': (lambda self, cr, uid, ids,
                                                                       c={}: ids,['cost_price', 'product_uom_qty',
                                                                                  "price_unit", 'cost_tax',
                                                                                  'has_input_invoice'],
                                                                10),
                                            'stock.picking': (_get_all_invoiced_order, ['state'], 10),
                                        },copy=False
                                        ),
        'is_gross_profit_checked': fields.boolean(u'毛利已核对', copy=False, select=True),
        'gross_profit_checked_time': fields.datetime(u'毛利核对时间', copy=False),
        'gross_profit_rate': fields.float(string=u'毛利率（%）', readonly=1, copy=False),
        'profit_deduct_type_id': fields.many2one('profit.deduct.type', string='毛利提成类型', copy=False, readonly=1),
        'sales_staff_commission': fields.float(string='销售提成', type='float', copy=False),
        'sales_assistant_commission': fields.float(string='助理提成', type='float', copy=False),
        'minimum_price': fields.float(string='最低售价', copy=False),
        'prod_spec': fields.text(u'型号备注', copy=False),
        'product_uom_qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoS')),
        'expected_delivery_time': fields.related('order_id', 'expected_delivery_time', type='date', string=u'预计发货日期', readonly="1", copy=False),
        'is_direct_delivery': fields.boolean(u'是否直送', copy=False),
        'real_so_id': fields.related('order_id', 'real_so_id', type='many2one', relation='sale.order', copy=False),
        'check_uid': fields.many2one('res.users', u'核对人', copy=False),
        'origin_price': fields.float(u'原价', copy=False),
        'discount_rate': fields.function(_compute_discount, type='float', string=u'折扣率', copy=False),
        'cost_price_subtotal': fields.function(_compute_cost_price_subtotal, string=u'成本价小计', type='float', copy=False,
                                            store={ 'sale.order.line': (lambda self, cr, uid, ids, c={}: ids,
                                                                        ['cost_price', 'product_uom_qty'], 10),
        }),
        'partner_id': fields.related('order_id', 'partner_id', relation="res.partner", type='many2one', string='客户', readonly='1',copy=False),
        'order_name': fields.related('order_id', 'name', type='char', string='订单号', readonly='1'),
        'is_card': fields.related('order_id', 'is_card',  type='boolean', string='是卡券订单',
                                     readonly='1', copy=False),
        'order_user_id': fields.related('order_id', 'user_id', relation="res.users", type='many2one', string='销售员', readonly='1'),
        'order_note': fields.related('order_id', 'note', type='char', string='订单备注', readonly='1'),
        'order_confirm_date': fields.related('order_id', 'date_order', type='datetime', string='首次确认日期', readonly='1', store=True),
        'category_id': fields.related('product_id', 'categ_id', type='many2one', relation='product.category',
                                      string='产品分类', readonly='1', store=True),
        'cust_grade': fields.related('order_id', 'cust_grade', type='char', string='客户级别', readonly='1', store=True),

        'prodcut_prod_spec': fields.related('product_id', 'prod_spec', type='char', string='产品规格', readonly='1'),
        'prod_model': fields.related('product_id', 'prod_model', type='char', string=u'产品型号', readonly="1"),
        'origin_prod_spec': fields.related('product_id', 'prod_spec', type='char', string='产品规格', readonly=True,
                                           store=True),
        'product_id_type':fields.related('product_id','type',type='char',string='产品类型',copy=False),
        'sale_note':fields.char(u'销售备注', copy=False),
        'has_input_invoice': fields.boolean(u'是否有进项发票', copy=False),
        'cost_tax': fields.float(u'税成本', copy=False), #没有进项默认6%
        'cost_packing': fields.float(u'包装成本', copy=False),
        'cost_logistics': fields.float(u'物流成本', copy=False),
        'purchase_uid': fields.many2one('res.users', string='采购员', copy=False),
        'locked_quant_qty': fields.function(_compute_locked_quant_qty, type='float', string='已锁到销售单库存数', copy=False,
                                            digits_compute=dp.get_precision('Product Unit of Measure'),
                                           store={'stock.quant': (_get_locked_quant, ['so_line_id', 'reservation_id'], 10 )}),
        'salesman_id': fields.function(get_salesman_id, type='many2one', relation='res.users', store={
            'sale.order.line': (lambda self, cr, uid, ids, c={}: ids,
                                ['product_id'], 10),
            'sale.order': (get_change_salesman_order_line, ['user_id'], 10),
        }, string='销售员'),
        'share_to_uid': fields.function(get_share_to_uid, type='many2one', relation='res.users', store={
            'sale.order.line': (lambda self, cr, uid, ids, c={}: ids,
                                ['product_id'], 10),
            'sale.order': (get_change_salesman_order_line, ['user_id', 'share_to_uid'], 10),
        }, string='助理', select=True),
        'purchase_processed': fields.function(_compute_purchase_processed, string=u'采购已处理', type='boolean',
                                              fnct_search=_purchase_processed_search),
        'stock_out_locked_quant': fields.function(_compute_stock_out_locked_quant, string=u'锁定数量', type='integer',
                                        help='出库单锁定数量（assign之后保留的数量）'),
        'purchase_users': fields.function(_get_all_purchase_user, string='采购员', type='char')
    }
    _defaults = {
        'delivered_qty': 0,
        'has_input_invoice': True,
        'cost_tax': 0,
        'shipped_qty': 0,
        'locked_quant_qty': 0,
        'gross_profit_checked_time': False,
    }


class sale_order_temporary(osv.osv):
    _name = 'sale.order.temporary'
    _columns = {
        'sale_id': fields.many2one('sale.order', u'销售订单'),
        'sale_order_dict': fields.char(u'订单明细'),
    }


class confirm_out_going_wizard(models.TransientModel):
    _name = 'confirm.out.going.wizard'
    _description = u'确认出库的数量'

    def get_order_product_message(self, cr, uid, context=None, location_id=False,):
        # 1.获取到销售订单中的本次发货数量2.对比剩余库存显示出是否库存可用
        sale_order_obj = self.pool.get("sale.order").browse(
            cr, uid, context.get("default_sale_order_id"), context=context)
        # stock_quant = self.pool.get("stock.quant")
        prodcut_message = "<table class='oe_webclient'><tr><td>产品</td><td>本次出库&入库数量</td><td>库存数</td><td>是否可用</td></tr>"
        # str("产品名字").center(45) + str("订单数量").center(25) + str(
        #     "即将出/入库数").center(25) + str("可用库存数").center(25) + str("库存状态").center(25)
        is_ok = ""
        qty_not_zero_flag = False
        product_id_order_dcit = {}
        for order_line in sale_order_obj.order_line:
            is_ok = "可用"
            if order_line.delivery_quantity_qty!=0 and order_line.product_id.type!='service':
                product_id_order_dcit.update({order_line.product_id.id: order_line})
        if location_id:
            location_ids = self.pool.get('stock.location').get_child_location_ids(cr, uid, location_id, context=context)
        else:
            location_ids = self.pool.get('sale.order').get_sale_order_location_id(cr, uid, sale_order_obj.id, context=context)[1]
        result_dict = self.pool.get(
            'product.product').get_product_location_available_qty(cr, uid, product_id_order_dcit.keys(),
                                                                  so_id=sale_order_obj.id, context=context)
        for product_id, order_line in product_id_order_dcit.items():
            available_without_reservation = sum([result_dict.get((product_id, order_line.prod_spec or None, location_id),0)
                                                 for location_id in location_ids])
            if order_line.delivery_quantity_qty != 0 and not qty_not_zero_flag:
                qty_not_zero_flag = True
            if order_line.product_uom_qty > 0:
                product_bom_ids = self.pool.get('mrp.bom').search(cr, uid, [('product_tmpl_id', '=', product_id)],
                                                                  context=context)
                if available_without_reservation >= order_line.delivery_quantity_qty or product_bom_ids:
                    is_ok = "可用"
                else:
                    is_ok = "库存不足"  # 用来标记是否库存可用
                prodcut_message = prodcut_message + u"<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (str(order_line.product_id.name), str(
                    order_line.delivery_quantity_qty), str(available_without_reservation), str(is_ok))
            else:
                is_ok = "可用"
                prodcut_message = prodcut_message + "<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>" % (str(order_line.product_id.name), str(
                    order_line.delivery_quantity_qty), str(available_without_reservation), str(is_ok))
        if not qty_not_zero_flag:
            prodcut_message = prodcut_message + u"<tr><td>本次出入库数量绝对值之和为零没必要出库</td></tr>"
        prodcut_message = prodcut_message + "</table>"
        return prodcut_message

    def on_change_location_id(self, cr, uid, ids, location_id, context=None):
        message = self.get_order_product_message(cr, uid, location_id=location_id, context=context)
        return {
            'value': {'product_message': message,
                      'picking_ok':  False if message.find('没必要') >= 0 else True,
                      'picking_is_ok':  False if message.find('库存不足') >= 0 or message.find('绝对值之和为零') >= 0 else True,
                      }
        }

    def _get_locaiton_id(self, cr, uid, context=None):
        order_obj = self.pool.get('sale.order')
        location_id = order_obj.get_sale_order_location_id(cr, uid, context.get("default_sale_order_id"),
                                                           context=context)[0]
        return location_id

    _columns = {
        'sale_order_id': fields.many2one('sale.order', u'确认出库的订单'),
        'location_id': fields.many2one('stock.location', u'库位'),
        'product_message': fields.html(u'订单明细检查', readonly=True),
        'picking_is_ok': fields.boolean(u"是否可出库"),
        'picking_ok': fields.boolean(u"ok"),
        'po_id': fields.many2one('purchase.order', '采购单')
    }
    _defaults = {
        'sale_order_id': lambda s, cr, uid, ctx={}: ctx.get("sale_order_id"),
        'product_message': get_order_product_message,
        'picking_ok': lambda s, cr, uid, ctx={}: False if (s.get_order_product_message(cr, uid, ctx)).find('没必要') >= 0 else True,
        'location_id': _get_locaiton_id,
        # 根据
        # get_order_product_message返回的信息进行判断是否库存可用（绝对值根据返回的字符串进行判断出入库的数量是否大于零），决定是否可以发货
        # //这是随后
        'picking_is_ok': lambda s, cr, uid, ctx={}: False if (s.get_order_product_message(cr, uid, ctx)).find('库存不足') >= 0 or (s.get_order_product_message(cr, uid, ctx)).find('绝对值之和为零') >= 0 else True,
    }

    def get_temporary_sale_order_type(self, cr, uid, order_row, context=None):
        out_move_line, in_move_line, temporary_is_return, temporary_is_exchange = False, False, False, False
        is_done = True
        for order_line in order_row.order_line:
            if order_line.product_id.type == 'service':
                continue
            if order_line.undelivered_qty != 0 and is_done:
                is_done = False
            if order_line.delivery_quantity_qty != 0 and order_line.product_uom_qty > 0:
                out_move_line = True
            elif order_line.delivery_quantity_qty != 0 and order_line.product_uom_qty < 0:
                in_move_line = True
        if out_move_line and in_move_line:
            temporary_is_exchange = True
        elif not out_move_line and in_move_line:
            temporary_is_return = True
        elif not out_move_line and not in_move_line:
            raise osv.except_osv(u'错误', u"没有需要生成出入库单的订单行！")
        return temporary_is_return, temporary_is_exchange
    ####################################################################################################################
    #生成空的picking 并返回对应的参数
    #
    def create_blank_picking(self, cr, uid, sale_order_row, temporary_is_return, temporary_is_exchange,
                             context=None, picking_vals={}):
        picking_out_id, picking_in_id = False, False
        stock_picking_type_obj = self.pool.get("stock.picking.type")
        stock_location_obj = self.pool.get("stock.location")
        stock_picking_obj = self.pool.get("stock.picking")
        sale_order_obj = self.pool.get("sale.order")
        picking_type_out_id = stock_picking_type_obj.search(cr, uid, [('code', '=', 'outgoing')], context=context)
        picking_type_in_id = stock_picking_type_obj.search(cr, uid, [('code', '=', 'incoming')], context=context)
        #如果是样品订单,则出入库从样品库出入
        if sale_order_row.is_sample:
            picking_type_out_id = stock_picking_type_obj.search(cr, uid, [('code', '=', 'sample_outgoing')], context=context)
            picking_type_in_id = stock_picking_type_obj.search(cr, uid, [('code', '=', 'sample_incoming')], context=context)
        sale_order_obj.check_expected_time_confirmed(cr, uid, sale_order_row, context=context)
        picking_type_out_row = stock_picking_type_obj.browse(
            cr, uid, picking_type_out_id, context=context)[0]  # warehouse_id
        picking_type_in_row = stock_picking_type_obj.browse(
            cr, uid, picking_type_in_id, context=context)[0]  # warehouse_id
        res = {"partner_id": sale_order_row.partner_id.id, 'so_id': context.get("default_sale_order_id"),
                "origin": sale_order_row.name, "picking_type_id": picking_type_out_id[0],
                "move_type": "one", 'consignee': sale_order_row.consignee, 'street': sale_order_row.street,
                'zip': sale_order_row.zip, 'mobile_number': sale_order_row.mobile_number,
                'phone_number': sale_order_row.phone_number, 'province_id': sale_order_row.province_id.id,
                'city_id': sale_order_row.city_id.id, 'county_id': sale_order_row.county_id.id,
                }
        res.update(picking_vals)
        stock_location_ids = stock_location_obj.search(cr, uid, [('usage', '=', 'customer')], context=context)
        if stock_location_ids:
            stock_customer_location_id = stock_location_ids[0]
        else:
            stock_customer_location_id = False
        res.update({"shipping_type_sale": sale_order_row.shipping_type_sale})
        if not temporary_is_return:
            # 发货单建立所需的数据
            # vals['name'] = self.pool.get('ir.sequence').get_id(cr, user,
            # sequence_id, 'id', context=context)
            res.update({'name': self.pool.get('ir.sequence').get_id(
                cr, uid, picking_type_out_row.sequence_id.id, 'id', context=context)})
            picking_out_id = stock_picking_obj.create(
                cr, uid, res, context=context)
        if temporary_is_exchange or temporary_is_return:
            # 退货单或者换货单所需建立的收货单
            res.update({"picking_type_id": picking_type_in_id[0], 'name': self.pool.get(
                'ir.sequence').get_id(cr, uid, picking_type_in_row.sequence_id.id, 'id', context=context)})
            picking_in_id = stock_picking_obj.create(
                cr, uid, res, context=context)
        return picking_in_id, picking_out_id, stock_customer_location_id,\
               picking_type_in_row, picking_type_in_id, picking_type_out_id

    ####################################################################################################################
    #
    #  出库助手点击确定出入库
    #  context的 on_move 为True时表示准备移动,否则确认移动
    #
    def confirm_out_going(self, cr, uid, ids, context=None):
        # 根据订单的
        stock_picking_type_obj = self.pool.get("stock.picking.type")
        sale_order_obj = self.pool.get("sale.order")
        default_sale_order_id = context.get("default_sale_order_id")
        sale_order_row = sale_order_obj.browse(cr, uid, default_sale_order_id, context=context)
        sale_order_line_obj = self.pool.get("sale.order.line")
        stock_move_obj = self.pool.get("stock.move")
        # stock_location_direct 直送库位相关的情况
        model_data_obj = self.pool.get('ir.model.data')
        direct_location_id = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.stock_location_direct')

        wizard_row = self.browse(cr, uid, ids, context=context)
        wizard_location_id = wizard_row.location_id and wizard_row.location_id.id
        picking_vals, is_direct_delivery = {}, False
        if direct_location_id == wizard_location_id:
            #出库单上表现出采购单的信息
            is_direct_delivery = True
            picking_vals = {
                'is_direct_delivery': True,
                'direct_delivery_po_id': wizard_row.po_id and wizard_row.po_id.id,
            }
        move_line_in_ids, move_line_out_ids, is_done = [], [], True
        temporary_is_return, temporary_is_exchange = self.get_temporary_sale_order_type(cr, uid, sale_order_row,
                                                                                        context=context)
        picking_in_id, picking_out_id, stock_customer_location_id, picking_type_in_row,\
        picking_type_in_id, picking_type_out_id = self.create_blank_picking(cr, uid, sale_order_row, temporary_is_return,
                                                        temporary_is_exchange, context=context, picking_vals=picking_vals)
        picking_type_out_row = stock_picking_type_obj.browse(
            cr, uid, picking_type_out_id, context=context)[0]  # warehouse_id
        picking_type_in_row = stock_picking_type_obj.browse(
        cr, uid, picking_type_in_id, context=context)[0]  # warehouse_id
        #取得样品订单"样品领用后归还"路由id
        model_data_obj = self.pool.get('ir.model.data')
        route_sample_return = model_data_obj.xmlid_to_res_id(cr, SUPERUSER_ID, 'dftg_ext.route_sample_return')
        procurement_obj = self.pool.get('procurement.order')
        proc_ids = []
        #建立出库单行
        for order_line in sale_order_row.order_line:
            if order_line.product_id.type == 'service':
                continue
            move_line_out_data = {}
            move_line_in_data = {}

            if order_line.undelivered_qty != 0 and is_done:
                is_done = False
            if order_line.delivery_quantity_qty != 0 and order_line.product_uom_qty > 0:
                move_line_out_data.update({'partner_id': sale_order_row.partner_id.id,
                                            "picking_type_id": picking_type_out_id[0],
                                           "product_uom_qty": order_line.delivery_quantity_qty,  # default_location_src_idfsdfefwfedfsdssdd
                                           "product_id": order_line.product_id.id,
                                           "name": order_line.name,
                                           'is_direct_delivery': is_direct_delivery,
                                           "location_dest_id": picking_type_out_row.default_location_dest_id.id,
                                           "location_id": wizard_location_id or
                                                          sale_order_row.order_type_id and
                                                          sale_order_row.order_type_id.stock_location_id.id or
                                                          picking_type_out_row.default_location_src_id.id,
                                           "picking_id": picking_out_id,
                                           "product_uom": order_line.product_id.uom_id.id, 'origin': sale_order_row.name,
                                           'so_line_id': order_line.id,'prod_spec': order_line.prod_spec})
                #如果是样品订单，则需要设置"样品领用后归还"路由
                # if sale_order_row.is_sample:
                #     move_line_out_data.update({'route_ids': [(4, route_sample_return)]})
                sale_order_line_obj.write(cr, uid, order_line.id, {"delivered_qty": order_line.delivered_qty +
                                                                                    order_line.delivery_quantity_qty,
                                                                   "delivery_quantity_qty": order_line.undelivered_qty
                                                                                            - order_line.delivery_quantity_qty},
                                          context=context)
                new_out_move_line_id = stock_move_obj.create(cr, uid, move_line_out_data, context=context)
                move_line_out_ids.append(new_out_move_line_id)

            elif order_line.delivery_quantity_qty != 0 and order_line.product_uom_qty < 0:
                move_line_in_data.update({'partner_id': sale_order_row.partner_id.id,
                                          "picking_type_id": picking_type_in_id[0],
                                          "product_uom_qty": abs(order_line.delivery_quantity_qty),  # default_location_src_id
                                          "product_id": order_line.product_id.id, "name": order_line.name,
                                          'is_direct_delivery': is_direct_delivery,
                                          "location_dest_id": wizard_location_id or
                                                              sale_order_row.order_type_id and
                                                              sale_order_row.order_type_id.stock_location_id.id or
                                                              picking_type_in_row.default_location_dest_id.id,
                                          "location_id": stock_customer_location_id or
                                                         picking_type_in_row.default_location_src_id.id,
                                          "picking_id": picking_in_id,
                                          'so_line_id': order_line.id,
                                          "product_uom": order_line.product_id.uom_id.id, 'origin': sale_order_row.name,
                                          })
                sale_order_line_obj.write(cr, uid, order_line.id, {"delivered_qty": abs(order_line.delivered_qty) +
                                                                                    abs(order_line.delivery_quantity_qty),
                                                                   "delivery_quantity_qty": abs(order_line.undelivered_qty)
                                                                                            - abs(order_line.delivery_quantity_qty)},
                                          context=context)
                move_line_in_ids.append(stock_move_obj.create(cr, uid, move_line_in_data, context = context))
        # 生成准备样品退回的move
        #procurement_obj.run(cr, uid, proc_ids, context=context)
        # if sale_orders.is_exchange:
        #     # 如果是换货单，要求入库单完成后，才会在就绪中能够看到，出库单
        #     stock_move_obj.write(cr, uid, move_line_in_ids, {
        #                          'move_dest_id': move_line_out_ids[0]}, context=context)
        if is_done:
            # 如果出库完成就回写销售订单 已发货完成 字段
            self.pool.get("sale.order").write(cr, uid, context.get(
                "default_sale_order_id"), {"shipment_completion": True} , context=context)
        self.handle_picking(cr, uid, temporary_is_return, temporary_is_exchange, picking_in_id,
                            picking_out_id, context=context)
        return self.return_picking_action(cr, uid, picking_in_id, picking_out_id, sale_order_row, context=context)

    def return_picking_action(self, cr, uid, picking_in_id, picking_out_id, sale_order_row, context=None):
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')
        result = mod_obj.get_object_reference(
            cr, uid, 'stock', 'action_picking_tree_ready')
        id = result and result[1] or False
        result = act_obj.read(cr, uid, [id], context=context)[0]
        res = mod_obj.get_object_reference(
            cr, uid, 'stock', 'view_picking_form')
        result['name'] = u'查看出/入库单'
        result['context'] = {}
        result['views'] = [(res and res[1] or False, 'form')]
        result['domain'] = "[('so_id','='," + str(sale_order_row.id) + ")　]"
        result['res_id'] = (picking_out_id and picking_in_id) and picking_in_id or picking_out_id or picking_in_id
        return result

    def handle_picking(self, cr, uid, temporary_is_return, temporary_is_exchange, picking_in_id,
                       picking_out_id, context=None):
        stock_picking_obj = self.pool.get('stock.picking')
        if temporary_is_return or temporary_is_exchange:
            # 如果是退货或者换货单 则进行确认
            stock_picking_obj.action_confirm(
                cr, uid, [picking_in_id], context=context)
        # 因为 picking_out_id 和 picking_in_id 是根据出入库单的型号进行填充的 所以，要判断型号才能够确定这个
        # 里面 有木有值 ，进行哪一步的操作
        if not temporary_is_return:
            stock_picking_obj.action_confirm(
                cr, uid, [picking_out_id], context=context)
            picking_state = stock_picking_obj.browse(
                cr, uid, picking_out_id, context=context)[0]
            if not temporary_is_exchange:
                if picking_state.state != "confirmed" and not context.get('on_move', False):
                    raise osv.except_osv(u'错误', u"库存不足,订单创建失败")
                stock_picking_obj.action_assign(
                    cr, SUPERUSER_ID, [picking_out_id], context)
                if picking_state.state != "assigned" and not context.get('on_move', False):
                    raise osv.except_osv(u'错误', u"库存不足,订单创建失败")
        if not context.get('on_move', False):
            # 如果时 on_MOVE是true 的时候就 直接确认 出入库或者入库
            pick_new = []
            if picking_in_id:
                pick_new.append(picking_in_id)
            if picking_out_id:
                pick_new.append(picking_out_id)
            for pick_id in pick_new:
                stock_picking_obj.do_enter_transfer_details(
                    cr, uid, [pick_id], context=context)
                transfer_details_ids = self.pool.get('stock.transfer_details').search(
                    cr, uid, [('picking_id', '=', pick_id)], context=context)
                #做真的移动(触发stock.py的action_done)
                self.pool.get('stock.transfer_details').do_detailed_transfer(
                    cr, uid, transfer_details_ids, context=context)
                # 返回生成的出入库单的form 或者tree视图
        return True

class product_template(osv.osv):
    _inherit = 'product.template'
    # 计算出产品上的预留数量 这样就方便计算 产品的 可用数量

    def compute_reservation_number(self, cr, uid, ids, virtual_available, args, context=None):
        """计算产品的保留数量"""
        res = {}
        reservation_qty = 0
        stock_quant = self.pool.get("stock.quant")
        stock_quant_ids = stock_quant.search(cr, uid, [(
            "product_id", "=", ids[0]), ("reservation_id", "!=", False)], context=context)
        for stock_quant_obj in stock_quant.browse(cr, uid, stock_quant_ids, context=context):
            reservation_qty += stock_quant_obj.qty
        res[ids[0]] = reservation_qty
        return res

    _columns = {
        'reservation_number': fields.function(compute_reservation_number, type="float", string='预留数量'),
    }


class allot_user_wizard(models.TransientModel):
    _name = 'allot.user.wizard'
    _columns = {
        'share_qty': fields.integer(u'本次分享客户的数量'),
        'user_id': fields.many2one('res.users', string='分配销售员'),
    }
    _defaults = {
        'share_qty': lambda s, c, u, ctx={}: len(ctx.get("active_ids", [])),
    }

    def allot_user(self, cr, uid, ids, context=None):
        # 批量 分配 客户给 销售员
        partner_obj = self.pool.get('res.partner')
        allot_user = self.browse(cr, uid, ids, context=context)
        partner_obj.write(cr, uid, context.get('active_ids'), {
                          'user_id': allot_user.user_id.id}, context=context)
        partner_ids = []
        for partner in partner_obj.browse(cr, uid, context.get('active_ids'), context=context):
            partner_ids.extend(
                [partner_contact.contacts_id.id for partner_contact in partner.child_contacts_ids])
        partner_obj.write(cr, uid, partner_ids, {
                          'user_id': allot_user.user_id.id}, context=context)
        return True


class copy_fake_wizard(models.TransientModel):
    _name = 'copy.fake.wizard'
    _description = u'有假单的创建'
    _columns = {
        'sale_order_char': fields.text(u'需要创建假单的销售订单'),
    }
    _defaults = {
        'sale_order_char': lambda self, cr, uid, context={}: '丶'.join([sale_order.name for sale_order in self.pool.get('sale.order').browse(cr, uid, context.get('active_ids'), context=context)]),
    }

    def fields_view_get(self, cr, uid, view_id=None, view_type='form',
                        context=None, toolbar=False, submenu=False):
        # 创建假单前，要进行判断，已有的假单销售订单，不允许创建。
        if context is None:
            context = {}
        res = super(copy_fake_wizard, self).fields_view_get(
            cr, uid, view_id=view_id, view_type=view_type, context=context, toolbar=toolbar, submenu=False)
        if context.get('active_model', '') == 'sale.order' and len(context['active_ids']) >= 1:
            list_partner, list_user = [], []
            for sale_order in self.pool.get('sale.order').browse(cr, uid, context['active_ids'], context=context):
                list_partner.append(sale_order.partner_id.id)
                list_user.append(sale_order.user_id.id)
                all_record = set(self.pool.get('sale.order').search(
                    cr, uid, [('real_so_id', '=', sale_order.id)], context=context))
                if len(all_record) >= 1:
                    raise osv.except_osv(_('Warning!'),
                                         u'%s销售订单已有假单' % (sale_order.name))
        return res

    def confirm_create_fake_sale_order(self, cr, uid, ids, context=None):
        # 根据所选的销售订单进行 生成假单
        sale_order_ids = []
        for sale_order_obj in self.pool.get("sale.order").browse(
                cr, uid, context.get('active_ids'), context=context):
            # 根据所选的销售订单获取到 要进行 复制的数据 （这里是一般复制的数据，对于本次的特殊需求，还会对数据进行改造）
            copy_data = self.pool.get("sale.order").copy_data(
                cr, uid, sale_order_obj.id, default=None, context=context)
            copy_data.update({'real_so_id': sale_order_obj.id,'shipment_completion': False,
                              'order_line_change_id': False, 'is_exchange': False, 'is_return': False, 'sale_order_invoice': False,
                              'print_state': False})
            sale_order_ids.append(self.pool.get("sale.order").create(
                cr, uid, copy_data, context=context))
        for sale_order_obj in self.pool.get("sale.order").browse(
                cr, uid, sale_order_ids, context=context):
            # 因为假单和销售订单是用的同一个 视图 ，为了避免  真单的判断，限制住假单的修改，因此要进行 费必要数据的清空
            self.pool.get("sale.order.line").write(cr, uid,
                                                   [order_line.id for order_line in sale_order_obj.order_line],
                                                   {'state': 'draft', 'undelivered_qty': 0, 'delivered_qty': 0, 'shipped_qty': 0, 'delivery_quantity_qty': 0,
                                                    'is_direct_delivery': False}, context=context)
        # 返回到 新生成的假单的界面
        mod_obj = self.pool.get('ir.model.data')
        act_obj = self.pool.get('ir.actions.act_window')
        result = mod_obj.get_object_reference(
            cr, uid, 'dftg_ext', 'fake_sale_order_action')
        id = result and result[1] or False
        result = act_obj.read(cr, uid, [id], context=context)[0]
        # compute the number of invoices to display

        if len(sale_order_ids) > 1:
            result[
                'domain'] = "[('id','in',[" + ','.join(map(str, sale_order_ids)) + "])]"
        else:
            res = mod_obj.get_object_reference(
                cr, uid, 'dftg_ext', 'fake_sale_order_form')
            result['views'] = [(res and res[1] or False, 'form')]
            result['res_id'] = sale_order_ids and sale_order_ids[0] or False
        return result


# class account_voucher(osv.osv):
#     _inherit = 'account.voucher'
#     _columns = {
#         'account_voucher_invoice': fields.many2one('purchase.invoice', string='收到发票申请'),
#     }
